home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / What's New? / Development Kits / USBDDK_v1.0.1_updated / Examples / USBSampleStorageDriver / StorageClassUTFunctions.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-09-29  |  94.5 KB  |  3,078 lines  |  [TEXT/MPS ]

  1. /*
  2.     File:        StorageClassUTFunctions.c
  3.  
  4.     Contains:    All device specific functions
  5.  
  6.     Version:    1.0
  7.  
  8.     Copyright:    © 1998 by Apple Computer, Inc., all rights reserved.
  9.  
  10. */
  11.  
  12. #include <Disks.h>
  13. #include <DriverGestalt.h>
  14. #include <Gestalt.h>
  15. #include <NameRegistry.h>
  16. #include <Power.h>
  17. #include <Resources.h>
  18. #include <Strings.h>
  19.  
  20. #include "StorageClassUTFunctions.h"
  21. #include "StorageClassUTDriver.h"
  22. #include "SampleStorageDriverAPI.h"
  23. #include "BlockDriverPriv.h"
  24. #include "SampleStorageVersion.h"
  25. #include "DriverIcons.h"
  26.  
  27. // These are making an early appearance here in the Driver.  We will add the support for these
  28. // now, and once Allegro ships, we will be ready.
  29. // These should be removed once the Universal Headers 3.2 are released.
  30. enum
  31. {
  32.     kdgPhysDriveIconSuite        = FOUR_CHAR_CODE('dics'),        /* Return a pointer to a IconFamily ('icns') data structure for */
  33.                                                                 /* Disk Driver physical drive (formerly in csCode 22) in driverGestaltResponse. */
  34.     kdgMediaIconSuite            = FOUR_CHAR_CODE('mics'),        /* Return a pointer to a IconFamily ('icns') data structure for */
  35.                                                                 /* Disk Driver media (formerly in csCode 21) in driverGestaltResponse. */
  36.                                                                 /* See IconServices.r for information detailing the 'icns' resource data format */
  37.     kdgMediaName                = FOUR_CHAR_CODE('mnam')        /* Return a pointer to a pascal string describing the Disk Driver (formerly in csCode 21) in driverGestaltResponse. */
  38. };
  39.  
  40. enum 
  41. {
  42.     kReadWriteRetryCount = 1,
  43.     kFormatRequestSenseRetryCount = 5
  44. };
  45.     
  46. typedef struct OurSleepQRec
  47. {
  48.     SleepQRec    theSleepQRec;
  49.     Boolean        isInSleep;
  50. } OurSleepQRec, *OurSleepQRecPtr;
  51.  
  52. typedef struct DriveRequestPB
  53. {
  54.     StorageExecuteCommandPB        executePB;
  55.     ParmBlkPtr                    theIOPB;
  56.     IOCommandID                 ioCommandID;
  57.     IOCommandKind                 ioCommandKind;
  58.     OSStatus                    status;
  59.     UInt16                        retryCount;
  60.     Boolean                        doWrite;
  61. } DriveRequestPB, *DriveRequestPBPtr;
  62.  
  63. typedef struct OurParamBlock
  64. {
  65.     DriveRequestPB                drivePB;
  66.  
  67.     // Global Control/Status fields for the driver
  68.     Boolean                        isFloppy;
  69.     Boolean                        isWriteProtected;
  70.     Boolean                        doInternalReadWrite;
  71.     Boolean                        diskInDrive;
  72.     UInt8                        currentExecutionState;
  73.     
  74.     // This is to workaround a bug in the PowerPC native version of the AddDrive
  75.     // call in systems before 8.5, where one needs to be added to the desired
  76.     // drive number before calling AddDrive.
  77.     Boolean                        addOneToAddDrive;
  78.  
  79.     SInt16                        drvrRefNum;            // Our driver reference number
  80.     ReadCapacityData            getCapacity;
  81.     UInt8                        sense[40];
  82.     DriveRec                    theDrive;
  83.     VolumeRec                    theVolume;
  84.     OurSleepQRec                theSleepQRec;
  85.     Str32                        DriveInfoString;
  86. } OurParamBlock;
  87.  
  88. // The values for the states of the state machines
  89. enum
  90. {
  91.     // Mount State Machine
  92.     kMountStartState = 1,
  93.     kMountTURDoneState,
  94.     kMountRequestSenseDoneState,
  95.     kMountGetGeometryDoneState,
  96.     kMountCheckGetGeometryErrorDone,
  97.     kMountReadPossibleCapacitiesDone,
  98.     kMountCheckWriteProtectDoneState,
  99.     kMountPreventRemovalDoneState,
  100.  
  101.     // Eject State Machine
  102.     kEjectStartState,
  103.     kEjectAllowRemovalDone,
  104.     kEjectCartridgeDone,
  105.  
  106.     // Format State Machine
  107.     kFormatStartState,
  108.     kFormatDoneState,
  109.     kFormatWaitDoneState,
  110.     kFormatRequestSenseDoneState,
  111.     kFormatGetGeometryDoneState,
  112.     kFormatCheckWriteProtectDoneState,
  113.     kFormatPreventRemovalDoneState,
  114.     
  115.     // Check for disk reinsertion
  116.     kReinsertionCheckStart,
  117.     kReinsertionTURDoneState,
  118.     kReinsertionRequestSenseDone
  119. };
  120.  
  121. /* Driver Control Codes */
  122. enum
  123. {
  124.     kcsSetBootPartitionCode            = 44,        // Set partition startup control <6/29/94>
  125.     kcsSetMountPartitionCode        = 45,        // Set partition mount control <6/29/94>
  126.     kcsSetWriteProtectCode            = 46,        // Set write protect control <6/29/94>
  127.     kcsClearMountPartitionCode        = 48,        // Clear partition mount control <6/29/94>
  128.     kcsClearWriteProtectCode        = 49,        // Clear write protect control <6/29/94>
  129.     kcsMountVolume                    = 60        // Mount volume control
  130. };
  131.  
  132. /* Driver Status Codes */
  133. enum
  134. {
  135.     kcsGetBootPartitionStatus        = 44,        // Startup CDev support <7/20/94>
  136.     kcsGetMountPartitionStatus        = 45,        // Drive Setup support <7/20/94>
  137.     kcsGetWriteProtect                = 46,        // Drive Setup support <7/20/94>
  138.     GetDrvCapacity                    = 125,        // get drive capacity
  139.     LastErrCode                        = 127,        // = 7F hex 
  140.     kcsGetMediaCapacity                = 128        // get media info (to support HFS+)
  141. };
  142.  
  143. // Apple driver specific 
  144. #define physIoCode        17
  145.  
  146. // Local Functions Prototypes
  147. void             InstallInSleepQueue(OurSleepQRecPtr ourSleepQRec);    
  148. void             RemoveFromSleepQueue(OurSleepQRecPtr ourSleepQRec);    
  149. long             SleepNotification(long message, SleepQRecPtr sleepRec);
  150.  
  151. // These supporting functions are in this file…
  152. OSStatus         MountSecondaryInterrupt( void *p1, void *p2);
  153. void             MountTheCartridge( void *theCurrentPB );
  154. void             EjectTheCartridge( void *theCurrentPB );
  155.  
  156. OSStatus         FormatCompletionInterrupt( void *p1, void *p2);
  157. void             FormatTheCartridge( void *theCurrentPB );
  158.  
  159. // ATAPI/SCSI-2 Device Commands
  160. OSStatus         CheckWriteProtect(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  161. OSStatus         TUR(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  162. OSStatus         GetMediaGeometry(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  163. OSStatus         ReadFormatCapacity(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  164. OSStatus        RequestSense(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  165. OSStatus         FormatFloppyCartridge(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  166. OSStatus         EjectCartridge(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion);
  167. OSStatus         PreventAllowRemoval(OurParamBlock *theCurrentPB, Boolean preventRemoval, StorageClassCompletionProcPtr ourCompletion);
  168.  
  169. OSStatus         ReinsertionSecondaryInterrupt( void *p1, void *p2);
  170. void             CheckForCartridgeReinsertion( void *theCurrentPB );
  171.  
  172. OSStatus         DoReadWriteCommand( OurParamBlock *theDriverPB, Boolean doWrite);
  173. OSStatus         ReadWriteBlock( OurParamBlock *theDriverPB, UInt32 startBlock, UInt32 numBlocks, Ptr buffer, Boolean doWrite, Boolean doAsync);
  174. void             ReadWriteCompletion( void *theDriverPB );
  175. void             WriteRequestSenseCompletion( void *theDriverPB );
  176. void             RequestSenseOnErrorCompletion( void *theDriverPB );
  177.  
  178. void             CheckUnexpectedRemoval(DriveRecPtr drive);
  179. void            RemoveVolume(DriveRecPtr drvRec, UInt16 volRef);
  180. Boolean            SetPowerMode(DriveRecPtr drive, UInt16 powerMode, UInt16 timerSecs);
  181. VolumeRecPtr    GetVolume(DriveRecPtr drive, UInt16 vRefNum, UInt32 partitionNum);
  182. VCB*            MountedVolOfDrive(DriveRecPtr drive);
  183. SInt16            NextQDrive();
  184. void             FlushDriveWriteCache( void );
  185. VolumeRecPtr    CreateVolume(DriveRecPtr drive, UInt32 partitionID, UInt32 volSize, UInt32 volOffset);
  186. void             RemoveDrive(DriveRecPtr theDrivePtr);
  187. void            UpdateQ(SInt16 qDrive, SInt32 newSize);
  188. SInt32            NextPartitionID(DriveRecPtr drive);
  189. void            SpinDownDrive(DriveRecPtr drive);
  190. Boolean         InstallDrive(DriveRecPtr drive, Boolean mountVols);
  191. OSErr             MountUnmountVolumes( DriveRecPtr drive );
  192.  
  193. //----------------------------------------------------------------------------------
  194. //                                    Globals
  195. //----------------------------------------------------------------------------------
  196.  
  197. static DriverLocationIcon                gDriveIcon;                            // static structure for control calls
  198. static StorageClassDispatchTablePtr        gItsTheDispatchTable = nil;            // The class's dispatch table
  199. static TimerID                            gInterruptTimer = 0;
  200. static OurParamBlock                    gTheParamBlock;
  201.  
  202.  
  203. //----------------------------------------------------------------------------------
  204. //                                    Driver calls
  205. //----------------------------------------------------------------------------------
  206.  
  207.  
  208. //    Always run at task level, can allocate and move memory.
  209.  
  210. OSStatus DriverInitializeCmd (    AddressSpaceID        addressSpaceID,
  211.                                 DriverInitInfoPtr    initialInfo)
  212. {
  213. #pragma unused (addressSpaceID, initialInfo)
  214.     
  215.     OSStatus    err = noErr;
  216.     UInt32        gestaltResponse;
  217.  
  218.     IfDebugging("\pInitialize Driver");    
  219.     gTheParamBlock.drvrRefNum = initialInfo->refNum;
  220.     gTheParamBlock.drivePB.theIOPB = nil;
  221.     gTheParamBlock.currentExecutionState = kMountStartState;
  222.     gTheParamBlock.doInternalReadWrite = false;
  223.     gTheParamBlock.diskInDrive = false;
  224.     
  225.     gTheParamBlock.isFloppy = false;
  226.  
  227.     // Check System version to see if we need to add one to AddDrive calls
  228.     Gestalt    (gestaltSystemVersion,(long *) &gestaltResponse);
  229.     if( (gestaltResponse&0xFFFF) >= 0x0850 )
  230.     {
  231.         // We are on system 8.5 or later, we do not need to add 1 to AddDrive calls
  232.         gTheParamBlock.addOneToAddDrive = false;
  233.  
  234.         // Set up our color icon family
  235.         BuildMediaIconFamily();
  236.     }
  237.     else
  238.     {
  239.         gTheParamBlock.addOneToAddDrive = true;
  240.     }
  241.  
  242.     // Clear out the Drive Record
  243.     BlockZero((Ptr) &gTheParamBlock.theDrive, sizeof(DriveRec));
  244.     
  245.     // Clear out the Volume Record
  246.     BlockZero((Ptr) &gTheParamBlock.theVolume, sizeof(VolumeRec));
  247.     
  248.     // Build the Drive Info string returned in Status/Control calls
  249.     {
  250.         Str32 tempStr;
  251.         
  252.         PStrCopy( gTheParamBlock.DriveInfoString, "\pUSB (v");
  253.         CStrToPStr(tempStr, kStorageStringVersShort); // driver version
  254.         PStrCat(gTheParamBlock.DriveInfoString, tempStr); // driver version
  255.         PStrCat(gTheParamBlock.DriveInfoString, "\p)");
  256.     }
  257.  
  258.     BlockZero((Ptr) &gTheParamBlock.theSleepQRec, sizeof(OurSleepQRec));
  259.     InstallInSleepQueue( &gTheParamBlock.theSleepQRec );
  260.     return (err);
  261. }
  262.  
  263. //    Always run at task level, can allocate and move memory.
  264.  
  265. OSStatus DriverFinalizeCmd (DriverFinalInfoPtr finalInfo)
  266. {
  267. #pragma unused (finalInfo)
  268.     OSStatus    err = noErr;
  269.  
  270.     DestroyMediaIconFamily();
  271.     RemoveFromSleepQueue( &gTheParamBlock.theSleepQRec );    
  272.     //DriverCloseCmd (nil);
  273.     
  274.     return (err);
  275. }
  276.  
  277. //    Always run at task level, can allocate and move memory.
  278.  
  279. OSStatus DriverSupersededCmd (DriverSupersededInfoPtr supersededInfo)
  280. {
  281. #pragma unused (supersededInfo)
  282.  
  283.     OSErr    err = noErr;
  284.  
  285.     return (err);
  286. }
  287.  
  288. //    Always run at task level, can allocate and move memory.
  289.  
  290. OSStatus DriverReplaceCmd (    AddressSpaceID            addressSpaceID,
  291.                             DriverReplaceInfoPtr    replaceInfo)
  292. {
  293. #pragma unused (addressSpaceID, replaceInfo)
  294.     OSStatus    err = noErr;
  295.  
  296.     return (err);
  297. }
  298.  
  299. //    Always run at task level, can allocate and move memory.
  300. OSStatus DriverOpenCmd ( AddressSpaceID    addressSpaceID, ParmBlkPtr pb)
  301. {
  302. #pragma unused (addressSpaceID, pb)
  303.     OSStatus    err         = noErr;
  304.  
  305.     // Let the world know we can do DriverGestalt calls
  306.     DriverGestaltOn(gTheParamBlock.drvrRefNum);
  307.     
  308.     return(err);
  309. }
  310.  
  311. //    Always run at task level, can allocate and move memory.
  312. OSStatus DriverCloseCmd (ParmBlkPtr pb)
  313. {
  314. #pragma unused (pb)
  315.     OSStatus        err = noErr;
  316.  
  317.     gTheParamBlock.diskInDrive = false;
  318.  
  319.     // If an interrupt timer is set, cancel it!
  320.     if( gInterruptTimer != 0 )
  321.     {
  322.         AbsoluteTime    timeLeft;
  323.         
  324.         // Cancel any pending timers
  325.         CancelTimer( gInterruptTimer, &timeLeft);
  326.         gInterruptTimer = 0;
  327.     }
  328.  
  329.  
  330.     // Force volume off line
  331.     gTheParamBlock.theDrive.busState = kOFFLINE;
  332.     CheckUnexpectedRemoval(&gTheParamBlock.theDrive);
  333.  
  334.     // If a command is pending, we should cancel it.
  335.     // Since we currently have no way of informing the Class driver to abort,
  336.     // we will wait for the class driver to tell us the command has finished.
  337.     while(    gTheParamBlock.drivePB.theIOPB != nil );
  338.     
  339.     // Dequeue all drive volumes 
  340.     RemoveDrive(&gTheParamBlock.theDrive);
  341.     (void) ((gItsTheDispatchTable)->pStorageControl)(kControlEnableRemoval, nil);
  342.     
  343.     return (err);
  344. }
  345.  
  346. //    May run at interrupt level, CANNOT allocate or move memory.
  347. OSStatus DriverControlCmd (    AddressSpaceID    addressSpaceID,
  348.                             IOCommandID        ioCommandID,
  349.                             IOCommandKind    ioCommandKind,
  350.                             ParmBlkPtr        pb)
  351. {
  352. #pragma unused ( addressSpaceID )
  353.     OSStatus        err            = noErr;
  354.     CntrlParamPtr    pbPtr;
  355.     UInt32            *altParams;                // local pointer to alternate control parameters
  356.     DriveRecPtr        drive         = &gTheParamBlock.theDrive;
  357.     VolumeRecPtr    vol         = nil;        // pointer to our volume record structure
  358.  
  359.     BlockZero((Ptr) &gTheParamBlock.drivePB, sizeof(DriveRequestPB));
  360.     
  361.     gTheParamBlock.drivePB.theIOPB            = nil;
  362.     gTheParamBlock.drivePB.ioCommandID         = ioCommandID;
  363.     gTheParamBlock.drivePB.ioCommandKind     = ioCommandKind;
  364.     gTheParamBlock.drivePB.retryCount         = 0;
  365.     gTheParamBlock.drivePB.doWrite             = false;
  366.         
  367.     pbPtr = (CntrlParamPtr) pb;
  368.     altParams = (UInt32 *)&pbPtr->csParam[0];        // alternate parameters
  369.  
  370.     // Parse the control codes…
  371.     //SysDebugStr("\pControl Call");
  372.     switch(pbPtr->csCode) 
  373.     {
  374.         case killCode:
  375.         {
  376.             // This driver does not support the killCode,
  377.             // return back -1 as per TechNote DV 17: Sony Driver
  378.             err = -1;
  379.         }
  380.         break;
  381.         
  382.         case kVerify:                        // Verify the media, this should only be called for floppies
  383.         {
  384.             if(gTheParamBlock.theDrive.capacity == 0)
  385.             {
  386.                 err = nsDrvErr;            // no Media is inserted, return an error
  387.             }
  388.             else if( gTheParamBlock.isFloppy == true )
  389.             {
  390.                 UInt16     diskCapacity;
  391.                 UInt16     startBlock;
  392.                 UInt16    numberBlocks = 16;
  393.                 UInt8    *blockBuffer;
  394.                 
  395.                 diskCapacity = gTheParamBlock.theDrive.capacity;
  396.                 blockBuffer = (UInt8 *) NewPtrSysClear(numberBlocks * gTheParamBlock.theDrive.blockSize);
  397.                 if(blockBuffer == nil)
  398.                 {
  399.                     err = verErr;
  400.                 }
  401.                 else
  402.                 {
  403.                     for (startBlock = 0; startBlock<diskCapacity; startBlock+=numberBlocks)
  404.                     {
  405.                         gTheParamBlock.doInternalReadWrite = true;
  406.                         err = ReadWriteBlock( &gTheParamBlock, startBlock, numberBlocks,(Ptr)  blockBuffer, false, false);
  407.                         gTheParamBlock.doInternalReadWrite = false;
  408.     
  409.                         // an Error has occured                    
  410.                         if ( err != noErr )
  411.                         {
  412.                             // report a verify error
  413.                             err = verErr;
  414.                             break;
  415.                         }
  416.                     }
  417.                     DisposePtr((Ptr) blockBuffer);
  418.                 }
  419.             }
  420.             else
  421.             {
  422.                 err = noErr;            // For the regular cartridge, just report noErr
  423.             }
  424.         }
  425.         break;
  426.  
  427.         case kFormat:
  428.         {
  429.             if (gTheParamBlock.isWriteProtected == true)
  430.             {
  431.                 // check for write protect
  432.                 err = wPrErr;
  433.                 break;
  434.             }
  435.             
  436.             if(gTheParamBlock.theDrive.capacity == 0)
  437.             {
  438.                 err = nsDrvErr;            // no Media is inserted, return an error
  439.             }
  440.             else if( gTheParamBlock.isFloppy == true )
  441.             {
  442.                 gTheParamBlock.currentExecutionState = kFormatStartState;
  443.                 FormatTheCartridge( &gTheParamBlock );
  444.                 err = gTheParamBlock.drivePB.status;
  445.             }
  446.             else
  447.             {
  448.                 err = noErr;            // For the regular cartridge, just report noErr
  449.             }
  450.         }
  451.         break;
  452.  
  453.         case kEject:
  454.         {
  455.             // We get this call whenever a volume is put away (dragged to trash).  
  456.             // We don't dequeue the DrvQEl or delete the drive record in case the 
  457.             // system may remount it later (via kcsMountVolume control call). 
  458.             // However, if the drive is ejectable (PCMCIA) we issue the eject of 
  459.             // the drive if there are no mounted (online) volumes for it.
  460.  
  461.             Boolean hasMountedVolume = (MountedVolOfDrive(drive) != nil);
  462.  
  463.             if (!hasMountedVolume)                    // If no more mounted volumes…
  464.             {
  465.                 //SpinDownDrive(drive);                // spin down drive to conserve power
  466.                 gTheParamBlock.currentExecutionState = kEjectStartState;
  467.                  EjectTheCartridge( &gTheParamBlock );
  468.             }
  469.             
  470.         }
  471.         break;
  472.  
  473.         case kSetTagBuffer:                    // This is a floppy specific control call
  474.         {            
  475.             err = controlErr;                // Return a controlErr, since we do not support this call
  476.         }
  477.         break;
  478.  
  479.         case kTrackCache:                    // This is a floppy specific control call
  480.         {            
  481.             err = noErr;                    // The driver does not keep an internal write cache,
  482.                                             // terfore, it will act like it does and return noErr
  483.         }
  484.         break;
  485.  
  486.         case physIoCode:                    // 1 = use physical offset, else use partition offset
  487.         {
  488.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  489.             if ( vol )                                        // If volume requested exists
  490.             {
  491.                 drive = (DriveRecPtr)vol->drivePtr;            // get its drive record
  492.             }
  493.             else                                            // else if drive was requested and exists
  494.             {
  495.                 if ( drive )
  496.                 {
  497.                     vol = drive->nextVol;                    // use first volume on drive
  498.                 }
  499.             }        
  500.     
  501.             if (!drive || !vol)                                // If we did not find a drive and volume
  502.             {
  503.                 return(nsDrvErr);                            // report drive not found error
  504.             }
  505.  
  506.             vol->curoffset = (*(UInt16 *)pbPtr->csParam == 1) ? 0 : vol->partoffset;
  507.         }
  508.         break;
  509.  
  510.         case kDriveIcon:                    // Return icon displayed during media initialization
  511.         case kMediaIcon:                    // Return icon displayed on desktop for media
  512.         {
  513.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  514.  
  515.             if ( !vol )                                // If we did not find a drive and volume
  516.             {
  517.                 err = nsDrvErr;                        // report drive not found error
  518.                 break;
  519.             }
  520.                 
  521.             BlockMove(vol->mediaIconPtr, &gDriveIcon.ataLocationIcon, sizeof(DiskIcon));
  522.  
  523.             // Copy over the DriveInfo string from the global param block        
  524.             PStrCopy(gDriveIcon.ataLocationString, gTheParamBlock.DriveInfoString);
  525.  
  526.             // Finally, return the pointer to the icon in csParam
  527.             *(DriverLocationIcon**)pbPtr->csParam = &gDriveIcon;    
  528.  
  529.             err = noErr;                // clear any error from above
  530.         }
  531.         break;
  532.  
  533.         case kDriveInfo:                    // DRIVE INFO request (was from ATA maanger)
  534.         {
  535.             pbPtr->csParam[0] = 0;            // Upper Word is always 0;
  536.             pbPtr->csParam[1] = 0;            // Clear Lower Word
  537.             
  538.             if( gTheParamBlock.isFloppy == true )
  539.             {
  540.                 // If we currently have a floppy loaded, return the following info
  541.                 pbPtr->csParam[1] =     ( 1 << 11 )     // Drive cardinality (0 - primary, 1 - secondary)
  542.                                     |    ( 0 << 10 )        // Media removability ( 0 - removable, 1 - fixed )
  543.                                     |     ( 0 << 9 )         // Interface ( 0 - floppy, 1 - SCSI )        
  544.                                     |     ( 1 << 8 )         // Location ( 0 - internal, 1 - external )
  545.                                                         // Bits 4,5,6,7 are reserved
  546.                                     |     4;                // Drive Type ( use 4 for SuperDrive for compatibility )
  547.             }
  548.             else if(gTheParamBlock.theDrive.blockSize != 0)
  549.             {
  550.                 // If we currently have a cartridge loaded, return the following info
  551.                 pbPtr->csParam[1] =     ( 1 << 11 )     // Drive cardinality (0 - primary, 1 - secondary)
  552.                                     |    ( 0 << 10 )        // Media removability ( 0 - removable, 1 - fixed )
  553.                                     |     ( 1 << 9 )         // Interface ( 0 - floppy, 1 - SCSI )        
  554.                                     |     ( 1 << 8 )         // Location ( 0 - internal, 1 - external )
  555.                                                         // Bits 4,5,6,7 are reserved
  556.                                     |     1;                // Drive Type ( use 1 for Unknown drive (Not a floppy) )
  557.             }
  558.  
  559.         }
  560.         break;
  561.  
  562.         
  563.         case kDriverConfigureCode:
  564.         {
  565.             DriverConfigParam             *configPtr;            // local pointer to drive config structure
  566.                         
  567.             configPtr = (DriverConfigParam *) pbPtr;
  568.             switch(configPtr->driverConfigureSelector) 
  569.             {
  570.                 case kdgFlush:
  571.                 {
  572.                     // Issue a Flush cache Command to the drive
  573.                     FlushDriveWriteCache();
  574.                 }
  575.                 break;
  576.                 
  577.                 default:
  578.                 {
  579.                     err = controlErr;
  580.                 }
  581.                 break;
  582.             }
  583.         }
  584.         break;
  585.  
  586.         case kcsSetBootPartitionCode:                // Set Startup Partition
  587.         case kcsSetMountPartitionCode:                // Set Mount Partition
  588.         case kcsClearMountPartitionCode:            // Clear Mount Partition
  589.         case kcsSetWriteProtectCode:                // Set Write Protect on Partition
  590.         case kcsClearWriteProtectCode:                // Clear Write Protect on Partition
  591.         {
  592.             err = controlErr;
  593.         }
  594.         break;
  595.  
  596.         case kRegisterPartition:            // Register New Partition
  597.         {
  598.             DrvQElPtr        theDrvQEl;            // drive queue element pointer
  599.             
  600.             // PCX will call this function when it wants to redefine a partition.  It will
  601.             // pass in the drive queue element pointer of the partition to redefine, the
  602.             // new starting physical block offset, and the new block length.
  603.  
  604.             vol = nil;            
  605.             err = nsDrvErr;                                            // assume an invalid volume
  606.             theDrvQEl = (DrvQElPtr) altParams[THE_DRIVE];
  607.             if ( theDrvQEl )                                        // if valid queue element pointer
  608.             {
  609.                 vol = GetVolume(drive, theDrvQEl->dQDrive, 0);
  610.                 if ( vol )                                            // and valid volume reference
  611.                 {
  612.                     vol->partoffset = altParams[THE_PHYS_START];    // new partition offset
  613.                     vol->curoffset = vol->partoffset;                // current offset changes also
  614.                     vol->partblks = altParams[THE_PHYS_SIZE];        // new partition size
  615.                     
  616.                     UpdateQ(theDrvQEl->dQDrive, vol->partblks);        // Update drive queue capacity
  617.                     vol->partmounted = true;                        // volume will be mounted by PCX
  618.                     err = noErr;                                    // clear error
  619.                 }
  620.             }
  621.         }
  622.         break;
  623.             
  624.         case kGetADrive:                    // Get A Drive (Create New Partition)
  625.         {            
  626.             // PCX calls this function to add a new partition.  The new partition's DrvQElPtr is
  627.             // returned. NOTE: If the driver handles multiple drives note that PCX does not pass
  628.             // in the physical drive number on which to create the new partition.  However, the
  629.             // DrvQElPtr stored at the pointer passed in is for another partition on the drive.
  630.         
  631.             if (!altParams[THE_VAR_QUEL])        // verify a valid queue element handle
  632.                 err = paramErr;
  633.             else
  634.             {
  635.                 // create a new volume record and DrvQEl associated with the physical drive.
  636.                 // The new volume starts at offset 0 and has no capacity yet. By default, the
  637.                 // partition will not have a partition map entry on the media.
  638.                     
  639.                 vol = CreateVolume(drive, NextPartitionID(drive), 0, 0 );
  640.                 if ( vol ) 
  641.                 {
  642.                     UInt16 volNumber;
  643.                     
  644.                     vol->vRefNum = NextQDrive();        // assign a logical drive number
  645.                     volNumber = vol->vRefNum;
  646.                     
  647.                     // Check to see if we need to add one to the AddDrive call
  648.                     if( gTheParamBlock.addOneToAddDrive == true)
  649.                     {
  650.                         volNumber += 1;
  651.                     }
  652.                     
  653.                     AddDrive(gTheParamBlock.drvrRefNum, volNumber,(DrvQElPtr)  &vol->driveStatus.qLink);
  654.  
  655.                     // Return the DrvQElPtr at the location passed in…
  656.                     *((DrvQElPtr*)altParams[THE_VAR_QUEL]) = (DrvQElPtr) &vol->driveStatus.qLink;
  657.                 }
  658.                 else
  659.                     err = controlErr;
  660.             }
  661.         }
  662.         break;
  663.             
  664.         case kcsMountVolume:                // Mount Volume
  665.         {
  666.             err = controlErr;
  667.         }
  668.         break;
  669.  
  670.         case kMediaPowerCSCode:                        // Set Power Mode
  671.         {
  672.             unsigned short powerMode = (UInt16) *((SInt8*)&(pbPtr->csParam[0]));
  673.                         
  674.             if (powerMode > kMediaModeOff)                // Verify a valid request
  675.             {
  676.                 err = paramErr;
  677.                 break;
  678.             }
  679.  
  680.             // Translate kMediaModeSuspend and kMediaModeOff to our spindown mode,
  681.             if ((powerMode == kMediaModeOff) || (powerMode == kMediaModeSuspend))
  682.             {
  683.                 SpinDownDrive(drive);
  684.             }
  685.             else if (drive->inSleepMode)
  686.             {
  687.                 // else all others (kMediaModeOn and kMediaModeStandBy) translate to our active mode.
  688.                 // If drive is in sleepMode, then wake it up, else assume active.
  689.             }
  690.  
  691.         }
  692.         break;
  693.  
  694.         case 500:
  695.         {
  696.             AbsoluteTime    theWait;
  697.         
  698.             IfDebugging("\pSet Dispatch Table");
  699.             gItsTheDispatchTable = ( StorageClassDispatchTablePtr ) *((UInt32 *)(&pbPtr->csParam[0]));                // The class's dispatch table
  700.  
  701.             (void) ((gItsTheDispatchTable)->pStorageControl)(kControlDisableRemoval, nil);
  702.             theWait = DurationToAbsolute(durationSecond);
  703.             theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  704.             err = SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  705.         }
  706.         break;
  707.  
  708.         case 21315:        // Disk copy format and copy
  709.         {
  710.             UInt16        theFormat;
  711.             UInt8        *theDiskImage;
  712.             
  713.             //SysDebugStr("\pDiskCopy");
  714.  
  715.             if( gTheParamBlock.isWriteProtected == true)
  716.             {
  717.                 err = wPrErr;            // disk is write protected
  718.                 break;
  719.             }
  720.             
  721.             theFormat = pbPtr->csParam[0];
  722.             theDiskImage = (UInt8 *) *((UInt32 *) &pbPtr->csParam[1]);        // Extract the pointer to the data
  723.             gTheParamBlock.doInternalReadWrite = true;
  724.             err = ReadWriteBlock( &gTheParamBlock, 0, gTheParamBlock.theDrive.capacity,(Ptr)  theDiskImage, true, false );
  725.             gTheParamBlock.doInternalReadWrite = false;
  726.  
  727.             // an Error has occured                    
  728.             if ( err != noErr )
  729.             {
  730.                 // report an error
  731.                 err = paramErr;
  732.             }
  733.         }
  734.         break;
  735.         
  736.         default:
  737.         {
  738.             err = controlErr;
  739.         }
  740.         break;
  741.     }
  742.         
  743.     //SysDebugStr("\pEnd Control Call");
  744.     return(err);
  745. }
  746.  
  747. //    May run at interrupt level, CANNOT allocate or move memory.
  748.  
  749. OSStatus DriverStatusCmd (    AddressSpaceID    addressSpaceID,
  750.                             IOCommandID        ioCommandID,
  751.                             IOCommandKind    ioCommandKind,
  752.                             ParmBlkPtr        pb)
  753. {
  754. #pragma unused (addressSpaceID, ioCommandID, ioCommandKind)
  755.  
  756.     OSStatus            err            = noErr;
  757.     CntrlParamPtr         pbPtr;
  758.     UInt32                *altParams;                // alternate csParams as long words
  759.     VolumeRecPtr        vol         = nil;
  760.     DriveRecPtr            drive         = &gTheParamBlock.theDrive;
  761.         
  762.     BlockZero((Ptr) &gTheParamBlock.drivePB, sizeof(DriveRequestPB));
  763.     
  764.     gTheParamBlock.drivePB.theIOPB            = nil;
  765.     gTheParamBlock.drivePB.ioCommandID         = ioCommandID;
  766.     gTheParamBlock.drivePB.ioCommandKind     = ioCommandKind;
  767.     gTheParamBlock.drivePB.retryCount         = 0;
  768.     gTheParamBlock.drivePB.doWrite             = false;
  769.  
  770.     pbPtr = (CntrlParamPtr) pb;
  771.     altParams = (UInt32 *) &pbPtr->csParam[0];            // alternate parameters
  772.  
  773.     // Get the drive and volume record of the logical drive specified.  Some status calls
  774.     // use ioVRefNum differently so verify a valid reference based upon the call.  We
  775.     // do the volume and drive validation here to avoid doing so for every function below.
  776.     //SysDebugStr("\pStatus Call");
  777.     switch(pbPtr->csCode) 
  778.     {
  779.         // These calls can optionally set ioVRefNum = 0 and use the partition ID in csParam[0]
  780.         // altParams[0] = partition block address;
  781.         case kcsGetBootPartitionStatus:
  782.         case kcsGetMountPartitionStatus:
  783.         case kcsGetWriteProtect:
  784.         case kcsGetMediaCapacity: // for HFS+
  785.         {
  786.             vol = GetVolume(drive, pbPtr->ioVRefNum, altParams[0]);
  787.  
  788.             if (!drive || !vol)                    // If we did not find a drive and volume
  789.                 return(nsDrvErr);                // report drive not found error
  790.         }
  791.         break;
  792.             
  793.         // Otherwise, assume ioVRefNum is valid as defined.
  794.         default:
  795.         {
  796.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  797.         }
  798.         break;
  799.     }
  800.  
  801.     switch(pbPtr->csCode)                     // Finish processing the status call…
  802.     {
  803.         case kReturnFormatList:
  804.         {
  805.             if( gTheParamBlock.isFloppy == false )
  806.             {
  807.                 err = statusErr;
  808.             }
  809.             else
  810.             {
  811.                 typedef struct FormatList
  812.                 {
  813.                     UInt32        NumberBlock;
  814.                     UInt8        TSSValid         : 1;
  815.                     UInt8        IsCurrentFormat : 1;
  816.                     UInt8        CanFormat        : 1;
  817.                     UInt8        Density            : 1;
  818.                     UInt8        NoSides            : 4;
  819.                     UInt8        SecPerTrack;
  820.                     UInt16        NumberTracks;
  821.                 } FormatList;
  822.                 
  823.                 UInt8            returnNumber;
  824.                 UInt8            totalFormats = 2;
  825.                 FormatList        theFormats[2];
  826.                 
  827.                 // Setup the Formats
  828.                 // Double Density Floppy Disk
  829.                 theFormats[0].NumberBlock        = 1440;
  830.                 theFormats[0].TSSValid            = 1;
  831.                 theFormats[0].Density            = 0;            // 0 means single density, 1 means Double Density
  832.                 theFormats[0].NoSides            = 2;
  833.                 theFormats[0].SecPerTrack        = 9;
  834.                 theFormats[0].NumberTracks        = 80;
  835.                 
  836.                 // High Density Floppy Disk
  837.                 theFormats[1].NumberBlock        = 2880;
  838.                 theFormats[1].TSSValid            = 1;
  839.                 theFormats[1].Density            = 1;
  840.                 theFormats[1].NoSides            = 2;
  841.                 theFormats[1].SecPerTrack        = 18;
  842.                 theFormats[1].NumberTracks        = 80;
  843.  
  844.                 if( drive->capacity < 0x600 )
  845.                 {
  846.                     theFormats[0].CanFormat            = 0;        // 0 means we can format this disk as 720K
  847.                     theFormats[1].CanFormat            = 1;        // 1 means we can not format this disk as 1.44M
  848.                     theFormats[0].IsCurrentFormat    = 1;         // 1 means 720K MFM Disk is installed
  849.                     theFormats[1].IsCurrentFormat     = 0;        // 0 means 1.44M MFM Disk is not installed
  850.                 }
  851.                 else
  852.                 {
  853.                     theFormats[0].CanFormat            = 1;        // 1 means we can not format this disk as 720K
  854.                     theFormats[1].CanFormat            = 0;        // 0 means we can format this disk as 1.44M
  855.                     theFormats[0].IsCurrentFormat    = 0;         // 0 means 720K MFM Disk is not installed
  856.                     theFormats[1].IsCurrentFormat     = 1;        // 1 means 1.44M MFM Disk is installed
  857.                 }
  858.  
  859.                 returnNumber = *((UInt16 *) &pbPtr->csParam[0]);
  860.                 if( totalFormats < returnNumber )
  861.                 {
  862.                     returnNumber = totalFormats;
  863.                 }
  864.                 
  865.                 BlockCopy( (Ptr) &theFormats[0], (Ptr) (*((UInt32 *) &pbPtr->csParam[1])), sizeof(FormatList) * returnNumber);
  866.                 *((UInt16 *) &pbPtr->csParam[0]) = returnNumber;
  867.             }
  868.         }
  869.         break;
  870.     
  871.         case kDriveStatus:
  872.         {
  873.             BlockMove(&vol->driveStatus, (UInt8 *) &pbPtr->csParam[0], sizeof(DrvSts));
  874.         }
  875.         break;
  876.     
  877.         case kMFMStatus:
  878.         {
  879.             if( gTheParamBlock.isFloppy == false )
  880.             {
  881.                 err = statusErr;
  882.             }
  883.             else
  884.             {
  885.                 pbPtr->csParam[0] = -3;                            // PC Industry standard MFM (no GCR support)
  886.                 pbPtr->csParam[1] = -1;                            // MFM Disk installed
  887.                 
  888.                 if( drive->capacity < 0x600 )
  889.                 {
  890.                     pbPtr->csParam[2] = 0;                        // 720K MFM Disk installed
  891.                 }
  892.                 else
  893.                 {
  894.                     pbPtr->csParam[2] = -1;                        // 1.44M MFM Disk installed
  895.                 }
  896.                 
  897.                 pbPtr->csParam[3] = -5;                            // Generic PC Floppy Disk Controller
  898.             }
  899.         }
  900.         break;
  901.     
  902.         case kDriverGestaltCode:
  903.         {
  904.             DriverGestaltParam             *gestaltPtr;            // local pointer to drive gestalt structure
  905.  
  906.             vol = GetVolume(drive, pbPtr->ioVRefNum, 0);
  907.             if (vol)
  908.                 drive = (DriveRecPtr) vol->drivePtr;            // get its drive record
  909.  
  910.             gestaltPtr = (DriverGestaltParam *) pbPtr;
  911.             switch(gestaltPtr->driverGestaltSelector) 
  912.             {
  913.                 case kdgVersion:
  914.                 {
  915.                     // Return information on the driver version
  916.                     NumVersion* numVersion;
  917.                     
  918.                     numVersion = GetDriverGestaltVersionResponse(gestaltPtr);
  919.                     
  920.                     numVersion->majorRev =            kStorageHexMajorVers;
  921.                     numVersion->minorAndBugRev =    kStorageHexMinorVers;
  922.                     numVersion->stage =                kStorageReleaseStage;
  923.                     numVersion->nonRelRev =            kStorageCurrentRelease;
  924.                 }
  925.                 break;
  926.                 
  927.                 case kdgDeviceType:
  928.                 {
  929.                     // Return the type of device--either floppy disk or removable disk
  930.                     DriverGestaltDevTResponse* deviceTypeResponse = GetDriverGestaltDevTResponse(gestaltPtr);
  931.  
  932.                     if( gTheParamBlock.isFloppy == true )
  933.                     {
  934.                         deviceTypeResponse->deviceType = kdgFloppyType;
  935.                     }
  936.                     else
  937.                     {
  938.                         deviceTypeResponse->deviceType = kdgRemovableType;
  939.                     }
  940.                 }
  941.                 break;
  942.                 
  943.                 case kdgInterface:
  944.                 {
  945.                     // Return the interface of the drive in ioVRefNum.
  946.                     DriverGestaltIntfResponse* interfaceResponse = GetDriverGestaltIntfResponse(gestaltPtr);        
  947.  
  948.                     interfaceResponse->interfaceType = 'USB ';
  949.                 }
  950.                 break;
  951.  
  952.                 case kdgSync:
  953.                 {
  954.                     // Return true if the driver supports only synchronous behavior
  955.                     DriverGestaltSyncResponse*    syncResponse = GetDriverGestaltSyncResponse(gestaltPtr);
  956.  
  957.                     syncResponse->behavesSynchronously = false;
  958.                 }
  959.                 break;
  960.                     
  961.                 case kdgBoot:
  962.                 {
  963.                     // Return the ID of the boot device for PRAM storage
  964.                     if (!drive || !vol)
  965.                         err = nsDrvErr;
  966.                     else
  967.                     {
  968.                         //DriverGestaltBootResponse* bootResponse = GetDriverGestaltBootResponse(gestaltPtr);
  969.                         
  970.                         //bootResponse->extDev =        0;
  971.                         //bootResponse->partition =    vol->partitionNo;
  972.                         //bootResponse->SIMSlot =        0;
  973.                         //bootResponse->SIMsRSRC =    0;
  974.                         err = statusErr;
  975.                     }
  976.                 }
  977.                 break;
  978.  
  979.                 case kdgWide:
  980.                 {
  981.                     // Return whether driver supports large volume addressing (> 4GByte)
  982.                     // Unless our cartridges are greater than 4 GB, we don't support
  983.                     // or need to support wide block addressing
  984.                     Boolean* response = GetDriverGestaltBooleanResponse(gestaltPtr);
  985.                     *response = false; 
  986.                 }
  987.                 break;
  988.  
  989.                 case kdgPurge:        // Return if we can be closed and purged from memory.
  990.                 {
  991.                     DriverGestaltPurgeResponse* response = GetDriverGestaltPurgeResponse(gestaltPtr);
  992.                                         
  993.                     // Currently the driver cannot be closed or purged once installed
  994.                     response->purgePermission = kmNoCloseNoPurge;
  995.  
  996.                     response->purgeDriverPointer = (Ptr) nil;
  997.                 }
  998.                 break;
  999.                     
  1000.                 case kdgSupportsSwitching:
  1001.                 {
  1002.                     // Return whether driver supports low power control call (csCode = 70h)
  1003.                     *(GetDriverGestaltBooleanResponse(gestaltPtr)) = false;
  1004.                 }
  1005.                 break;
  1006.                     
  1007.                 case kdgSupportsPowerCtl:
  1008.                 {
  1009.                     // Return whether driver supports low power control call (csCode = 70h)
  1010.                     *(GetDriverGestaltBooleanResponse(gestaltPtr)) = false;
  1011.                 }
  1012.                 break;
  1013.                     
  1014.                 case kdgAPI:
  1015.                 {
  1016.                     // Return whether driver supports PC-Exchange Control and Status calls
  1017.                     // related to partitioning.
  1018.                     DriverGestaltAPIResponse* apiResponse = GetDriverGestaltAPIResponse(gestaltPtr);
  1019.                     apiResponse->partitionCmds = true;
  1020.                 }
  1021.                 break;
  1022.                 
  1023.                 case kdgFlush:
  1024.                 {
  1025.                     // Return whether driver supports the Cache flush Control call,
  1026.                     // and whether the finder should tell us to flush our cache
  1027.                     DriverGestaltFlushResponse* response = GetDriverGestaltFlushResponse(gestaltPtr);
  1028.                     response->canFlush = true;
  1029.                     if(gTheParamBlock.theDrive.blockSize == 0)
  1030.                     {
  1031.                         response->needsFlush = false;     // Don't need a flush call because there is no media
  1032.                     }
  1033.                     else
  1034.                     {
  1035.                         response->needsFlush = true;     // Needs a flush call because media is present
  1036.                     }
  1037.                     response->canFlush = false;
  1038.                     response->needsFlush = false;     // Needs a flush call because media is present
  1039.                 }
  1040.                 break;
  1041.  
  1042.                 case kdgEject:
  1043.                 {
  1044.                     // Return whether driver wants eject call for shutdown and restart
  1045.                     // Eject on restart or shutdown
  1046.                     DriverGestaltEjectResponse* response = GetDriverGestaltEjectResponse(gestaltPtr);
  1047.                     response->ejectFeatures = 0L; // Clear both Dont Eject Bits, kShutDownDontEject and kRestartDontEject.
  1048.                 }
  1049.                 break;
  1050.  
  1051.                 case kdgVMOptions:
  1052.                 {
  1053.                     // Return whether drive can be used for Virtual Memory
  1054.                     // Don't support use of media for VM
  1055.                     DriverGestaltVMOptionsResponse* response = GetDriverGestaltVMOptionsResponse(gestaltPtr);
  1056.                     response->vmOptions = kAllowVMNoneMask;
  1057.                 }
  1058.                 break;
  1059.  
  1060.                 case kdgMediaInfo:
  1061.                 {
  1062.                     // Return back specific information about our media
  1063.                     DriverGestaltMediaInfoResponse* response = GetDriverGestaltMediaInfoResponse(gestaltPtr);
  1064.                     //response->numberBlocks =    vol->partblks;
  1065.                     response->numberBlocks =    gTheParamBlock.theDrive.capacity;
  1066.                     response->blockSize =        gTheParamBlock.theDrive.blockSize;
  1067.                     if(gTheParamBlock.theDrive.blockSize == 0)
  1068.                     {
  1069.                         response->mediaType =        kMediaTypeNoMedia;
  1070.                     }
  1071.                     else
  1072.                     {
  1073.                         response->mediaType =        kMediaTypeUnknown;
  1074.                     }
  1075.                 }
  1076.                 break;
  1077.  
  1078.                 /* Return a pointer to a IconFamily ('icns') data structure for */
  1079.                 /* Disk Driver physical drive (formerly in csCode 22) in driverGestaltResponse. */
  1080.                 case kdgPhysDriveIconSuite:
  1081.  
  1082.                 /* Return a pointer to a IconFamily ('icns') data structure for */
  1083.                 /* Disk Driver media (formerly in csCode 21) in driverGestaltResponse. */
  1084.                 case kdgMediaIconSuite:
  1085.                 {
  1086.                     // If the media is currently a floppy, return a statusErr so the system will
  1087.                     // handle the 3D Color icon information
  1088.                     if( gTheParamBlock.isFloppy == true )
  1089.                     {
  1090.                         if(FloppyMediaIconFamily == nil )
  1091.                         {
  1092.                             err = statusErr;
  1093.                         }
  1094.                         else
  1095.                         {
  1096.                             gestaltPtr->driverGestaltResponse = (UInt32) *FloppyMediaIconFamily;
  1097.                         }
  1098.                     }
  1099.                     else
  1100.                     {
  1101.                         if(CartridgeMediaIconFamily == nil )
  1102.                         {
  1103.                             err = statusErr;
  1104.                         }
  1105.                         else
  1106.                         {
  1107.                             gestaltPtr->driverGestaltResponse = (UInt32) CartridgeMediaIconFamily;
  1108.                         }
  1109.                     }
  1110.                 }
  1111.                 break;
  1112.  
  1113.                 case kdgMediaName:
  1114.                 {
  1115.                     /* Return a pointer to a pascal string describing the Disk Driver (formerly in csCode 21) in driverGestaltResponse. */
  1116.                     gestaltPtr->driverGestaltResponse = (UInt32) &gTheParamBlock.DriveInfoString;
  1117.                 }
  1118.                 break;
  1119.  
  1120.                 default: 
  1121.                     err = statusErr;        // unknown DriverGestalt selector
  1122.             }
  1123.         }
  1124.         break;
  1125.         
  1126.         case kcsGetBootPartitionStatus:     // Is this the boot partition?
  1127.         {
  1128.             // Since USB drives don't support booting, always report false
  1129.             pbPtr->csParam[0] = 0;
  1130.             err = statusErr;
  1131.         }
  1132.         break;
  1133.         
  1134.         case kcsGetMountPartitionStatus:     // Is this an automount partition?     
  1135.         {
  1136.             pbPtr->csParam[0] = 1;
  1137.         }
  1138.         break;
  1139.     
  1140.         case kcsGetWriteProtect:             // Is this partition write protected?
  1141.         {
  1142.             pbPtr->csParam[0] = (vol->driveStatus.writeProt) ? 1 : 0;
  1143.         }
  1144.         break;
  1145.     
  1146.         case kGetPartInfo:
  1147.         {
  1148.             // PCX will call this function to get info on the specified partition. 
  1149.             // Return the physical drive reference, the starting block offset, and 
  1150.             // the partition ID (any relative non-zero reference).
  1151.     
  1152.             partInfoRecPtr thePartInfo = (partInfoRecPtr)altParams[kPartInfoResponse];
  1153.     
  1154.             // SCSIID is not defined for non-SCSI devices.  For now we use the LUN only
  1155.             *((UInt32 *)&thePartInfo->SCSIID) = 0;
  1156.     
  1157.             thePartInfo->physPartitionLoc = vol->partoffset;
  1158.             thePartInfo->partitionNumber = vol->partitionNo;
  1159.         }
  1160.         break;
  1161.     
  1162.         case kMediaPowerCSCode:
  1163.         {
  1164.             // Return the current power mode of the drive.  NOTE: The power modes (kMediaModeOn,
  1165.             // kMediaModeStandBy, kMediaModeSuspend, and kMediaModeOff) are not the same as the ATA modes.
  1166.             // The primary difference is kMediaModeSuspend = Standby and kMediaModeStandBy = Idle
  1167.  
  1168.             UInt8 powerMode = kMediaModeOn;        // Assume drive is active
  1169.             
  1170.             if (drive->inSleepMode)             // check if in sleep mode
  1171.                 powerMode = kMediaModeOff;            
  1172.             
  1173.             // Return the power mode in the upper byte of csParam[0], lower byte is cleared
  1174.             pbPtr->csParam[0] = powerMode << 8;
  1175.         }
  1176.         break;
  1177.  
  1178.         case GetDrvCapacity:                        // Return physical drive capacity
  1179.         {
  1180.             pbPtr->csParam[0] = (SInt16)drive->capacity;                // lower word of capacity 
  1181.             pbPtr->csParam[1] = (SInt16)(drive->capacity >> 16);    // upper word of capacity
  1182.         }
  1183.         break;
  1184.         
  1185.         case kcsGetMediaCapacity:    // return media info for HFS+
  1186.         {
  1187.             //GetDriverGestaltMediaInfoResponse((DriverGestaltParam *) pbPtr)->numberBlocks =    vol->partblks;
  1188.             GetDriverGestaltMediaInfoResponse((DriverGestaltParam *) pbPtr)->numberBlocks =    gTheParamBlock.theDrive.capacity;
  1189.             GetDriverGestaltMediaInfoResponse((DriverGestaltParam *) pbPtr)->blockSize =    gTheParamBlock.theDrive.blockSize;
  1190.             GetDriverGestaltMediaInfoResponse((DriverGestaltParam *) pbPtr)->mediaType =    kMediaTypeNoMedia;
  1191.             if(gTheParamBlock.theDrive.blockSize == 0)
  1192.             {
  1193.                 GetDriverGestaltMediaInfoResponse((DriverGestaltParam *) pbPtr)->mediaType =        kMediaTypeNoMedia;
  1194.             }
  1195.             else
  1196.             {
  1197.                 GetDriverGestaltMediaInfoResponse((DriverGestaltParam *) pbPtr)->mediaType =        kMediaTypeUnknown;
  1198.             }
  1199.         }
  1200.         break;        
  1201.         
  1202.         case 17494:                            // DiskCopy version supported
  1203.         {
  1204.             pbPtr->csParam[0] = 0x0410;        // We support the Diskcopy 4.1 API
  1205.         }
  1206.         break;
  1207.         
  1208.         default:                            // Unrecognized status call
  1209.         {
  1210.             err = statusErr;
  1211.         }
  1212.         break;
  1213.     }
  1214.     //SysDebugStr("\pEnd Status Call");
  1215.     
  1216.     return(err);
  1217. }
  1218.  
  1219. //    May run at interrupt level, CANNOT allocate or move memory.
  1220.  
  1221. OSStatus DriverReadCmd (    AddressSpaceID    addressSpaceID,
  1222.                             IOCommandID        ioCommandID,
  1223.                             IOCommandKind    ioCommandKind,
  1224.                             ParmBlkPtr        pb)
  1225. {
  1226. #pragma unused ( addressSpaceID )
  1227.     OSStatus        err = ioErr;
  1228.  
  1229.     if( gTheParamBlock.diskInDrive == false)
  1230.     {
  1231.         err = ioErr;
  1232.     }
  1233.     else
  1234.     {
  1235.         //SysDebugStr("\pRead Call;g");
  1236.         // Clear out the Drive request PB
  1237.         BlockZero((Ptr) &gTheParamBlock.drivePB, sizeof(DriveRequestPB));
  1238.         
  1239.         gTheParamBlock.drivePB.theIOPB            = pb;
  1240.         gTheParamBlock.drivePB.ioCommandID         = ioCommandID;
  1241.         gTheParamBlock.drivePB.ioCommandKind     = ioCommandKind;
  1242.         gTheParamBlock.drivePB.retryCount         = 0;
  1243.         gTheParamBlock.drivePB.doWrite             = false;
  1244.     
  1245.         err = DoReadWriteCommand( &gTheParamBlock, false);
  1246.     }
  1247.     return(err);
  1248. }
  1249.  
  1250. //    May run at interrupt level, CANNOT allocate or move memory.
  1251.  
  1252. OSStatus DriverWriteCmd (    AddressSpaceID    addressSpaceID,
  1253.                             IOCommandID        ioCommandID,
  1254.                             IOCommandKind    ioCommandKind,
  1255.                             ParmBlkPtr        pb)
  1256. {
  1257. #pragma unused ( addressSpaceID )
  1258.     OSStatus        err = ioErr;
  1259.  
  1260.     if( gTheParamBlock.diskInDrive == false )
  1261.     {
  1262.         err = ioErr;
  1263.     }
  1264.     else
  1265.     {
  1266.         //SysDebugStr("\pWrite Call");
  1267.         // Clear out the Drive request PB
  1268.         BlockZero((Ptr) &gTheParamBlock.drivePB, sizeof(DriveRequestPB));
  1269.     
  1270.         gTheParamBlock.drivePB.theIOPB            = pb;
  1271.         gTheParamBlock.drivePB.ioCommandID         = ioCommandID;
  1272.         gTheParamBlock.drivePB.ioCommandKind     = ioCommandKind;
  1273.         gTheParamBlock.drivePB.retryCount         = 0;
  1274.         gTheParamBlock.drivePB.doWrite             = true;
  1275.             
  1276.         err = DoReadWriteCommand( &gTheParamBlock, true);
  1277.     }
  1278.     
  1279.     return(err);
  1280. }
  1281.  
  1282. //    May run at interrupt level, CANNOT allocate or move memory.
  1283.  
  1284. OSStatus DriverKillIOCmd (ParmBlkPtr pb)
  1285. {
  1286. #pragma unused (pb)
  1287.     OSStatus    err = noErr;
  1288.  
  1289.     return (err);
  1290. }
  1291.  
  1292. void InstallInSleepQueue(OurSleepQRecPtr ourSleepQRec)    
  1293. {
  1294.     Boolean hasPMDispatch;
  1295.     SInt32    status;
  1296.  
  1297.     // To do most power management we must have the Power Manager Dispatch routines.      
  1298.     if (Gestalt(gestaltPowerMgrAttr, &status) == 0)                                
  1299.         hasPMDispatch = (status & (1 << gestaltPMgrDispatchExists)) ? true : false;    
  1300.  
  1301.     if (hasPMDispatch)                                                            
  1302.     {
  1303.         // Power Manager manages a spindown timer for the internal drive.  When the
  1304.         // timer expires it calls all routines registered in the HD Queue, and then,
  1305.         // if PG&E is present (Powerbooks) it will turn off power to the drive.
  1306.         // If PG&E is not present (Desktops) Power Manager can't turn off power and
  1307.         // expects one of the queue routines to reduce drive power instead. 
  1308.         //  Therefore, if we manage the internal drive we should be in the HD Queue.
  1309.  
  1310.             ourSleepQRec->theSleepQRec.sleepQLink = nil;                                
  1311.             ourSleepQRec->theSleepQRec.sleepQType = sleepQType;                            
  1312.             ourSleepQRec->theSleepQRec.sleepQProc = NewSleepQProc( &SleepNotification);        
  1313.             //ourSleepQRec->theSleepQRec.sleepQProc = (SleepQUPP) NewSleepQProc( &SleepNotification);        
  1314.             ourSleepQRec->theSleepQRec.sleepQFlags = 0;                // reserved    
  1315.             ourSleepQRec->isInSleep = false;    
  1316.             SleepQInstall( (SleepQRecPtr) ourSleepQRec );                            
  1317.     }                                                                            
  1318. }                                                                                
  1319.  
  1320. void RemoveFromSleepQueue(OurSleepQRecPtr ourSleepQRec)    
  1321. {
  1322.     Boolean hasPMDispatch;
  1323.     SInt32    status;
  1324.  
  1325.     // To do most power management we must have the Power Manager Dispatch routines.      
  1326.     if (Gestalt(gestaltPowerMgrAttr, &status) == 0)                                
  1327.         hasPMDispatch = (status & (1 << gestaltPMgrDispatchExists)) ? true : false;    
  1328.  
  1329.     if (hasPMDispatch)                                                            
  1330.     {
  1331.         SleepQRemove( (SleepQRecPtr) ourSleepQRec );
  1332.     }
  1333. }                                                                                
  1334.  
  1335.  
  1336. long SleepNotification(long message, SleepQRecPtr sleepRec)
  1337. {
  1338.     OurSleepQRecPtr    ourSleepRec = (OurSleepQRecPtr) sleepRec;
  1339.     SInt32             response = noErr;                // assume we accept command
  1340.  
  1341.     switch(message)
  1342.     {
  1343.         case dozeRequest:                    // ##### Request to doze ######
  1344.         case dozeDemand:                    // ##### Going to doze now ######
  1345.         case sleepRequest:                    // ##### Request to sleep ######
  1346.         case sleepDemand:                    // ##### Going to sleep now ######
  1347.         case sleepNow:
  1348.             ourSleepRec->isInSleep = true;
  1349.             break;
  1350.  
  1351.         case sleepWakeUp:                    // ##### Wakeup from sleep ######
  1352.         case sleepRevoke:                    // ##### Someone denied sleep ######
  1353.         case dozeWakeUp:                    // ##### Wakeup from doze ######
  1354.             ourSleepRec->isInSleep = false;
  1355.             break;
  1356.     }
  1357.  
  1358.     return(response);
  1359. }
  1360.  
  1361.  
  1362. OSStatus MountSecondaryInterrupt( void *p1, void *p2)
  1363. {
  1364. #pragma unused ( p1, p2 )
  1365.     OSStatus             status;
  1366.     AbsoluteTime        theWait;
  1367.     UInt32                classDriverStatus;
  1368.  
  1369.     IfDebugging("\pMountSecondaryInterrupt");    
  1370.     gInterruptTimer = 0;
  1371.     
  1372.     if(gItsTheDispatchTable)
  1373.     {
  1374.         // Check if class driver is configured, if not setup another interrupt
  1375.         status = ((gItsTheDispatchTable)->pStorageStatus)(kStatusConfiguration, &classDriverStatus);
  1376.         if (status == noErr)
  1377.         {
  1378.             if ((classDriverStatus == kConfigureInProgress) || (gTheParamBlock.theSleepQRec.isInSleep == true))
  1379.             {
  1380.                 theWait = DurationToAbsolute(durationSecond);
  1381.                 theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1382.                 status = SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1383.                 
  1384.                 return noErr;
  1385.             } 
  1386.             else if (classDriverStatus == kConfigureComplete)
  1387.             {
  1388.                 gTheParamBlock.currentExecutionState = kMountStartState;
  1389.                 MountTheCartridge( &gTheParamBlock );
  1390.             }
  1391.         }
  1392.     }
  1393.     
  1394.     return noErr;
  1395. }
  1396.  
  1397.  
  1398. void MountTheCartridge( void *theCurrentPB )
  1399. {
  1400.     OSStatus         err = noErr;
  1401.     AbsoluteTime    oneSecondWait;
  1402.     DriveRec        *drive;
  1403.     OurParamBlock    *ourPB;
  1404.  
  1405.     ourPB = ((OurParamBlock *) theCurrentPB);
  1406.     drive = &ourPB->theDrive;
  1407.     oneSecondWait = DurationToAbsolute(durationSecond);
  1408.     //oneSecondWait = DurationToAbsolute(durationMillisecond * 20);
  1409.     switch (ourPB->currentExecutionState )
  1410.     {
  1411.         case kMountStartState:
  1412.         {
  1413.             ourPB->currentExecutionState = kMountTURDoneState;
  1414.             err = TUR( ourPB, &MountTheCartridge);
  1415.         }
  1416.         break;
  1417.         
  1418.         case kMountTURDoneState:
  1419.         {
  1420.             ourPB->currentExecutionState = kMountRequestSenseDoneState;
  1421.             err = RequestSense( ourPB, &MountTheCartridge);
  1422.         }
  1423.         break;
  1424.         
  1425.         case kMountRequestSenseDoneState:
  1426.         {
  1427.             if((ourPB->sense[12] == 0x00) && (ourPB->drivePB.executePB.status == noErr))
  1428.             {
  1429.                 ourPB->currentExecutionState = kMountGetGeometryDoneState;
  1430.                 ourPB->getCapacity.lastLogicalBlock = 0;
  1431.                 ourPB->getCapacity.blockLength = 0;
  1432.                 err = GetMediaGeometry( ourPB, &MountTheCartridge);
  1433.             }
  1434.             else
  1435.             {
  1436.                 ourPB->currentExecutionState = kMountStartState;
  1437.                 oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1438.                 err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1439.                 err = noErr;
  1440.             }
  1441.         }
  1442.         break;
  1443.  
  1444.         case kMountGetGeometryDoneState:
  1445.         {
  1446.             ReadCapacityData        *getGeometry;
  1447.             
  1448.             getGeometry = (ReadCapacityData *) &ourPB->getCapacity;
  1449.  
  1450.             drive->capacity = getGeometry->lastLogicalBlock+1; // Be sure to add the zero block in
  1451.             drive->blockSize = getGeometry->blockLength;
  1452.             
  1453.             if((drive->capacity == 0) || (drive->blockSize == 0))
  1454.             {
  1455.                 //Find out why the capacity is zero
  1456.                 ourPB->currentExecutionState = kMountCheckGetGeometryErrorDone;
  1457.                 err = RequestSense( ourPB, &MountTheCartridge);
  1458.                 break;
  1459.             }
  1460.  
  1461.             if(    drive->capacity >0x0C00)
  1462.             {
  1463.                 ourPB->isFloppy = false;
  1464.             }
  1465.             else
  1466.             {
  1467.                 ourPB->isFloppy = true;
  1468.             }
  1469.             
  1470.             ourPB->diskInDrive = true;
  1471.             
  1472.             // Check to see if media is write protected
  1473.             ourPB->currentExecutionState = kMountCheckWriteProtectDoneState;
  1474.             err = CheckWriteProtect(ourPB, &MountTheCartridge);
  1475.         }
  1476.         break;
  1477.  
  1478.         case kMountCheckGetGeometryErrorDone:
  1479.         {
  1480.             if( (ourPB->sense[12] == 0x30) && (ourPB->sense[13] == 0x01))
  1481.             {
  1482.                 // We have an unformatted cartridge, find out what format it can have
  1483.                 ourPB->currentExecutionState = kMountReadPossibleCapacitiesDone;
  1484.                 err = ReadFormatCapacity(ourPB, &MountTheCartridge);
  1485.             }
  1486.             else
  1487.             {
  1488.                 ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1489.                 oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1490.                 err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1491.             }
  1492.         }
  1493.         break;
  1494.         
  1495.         case kMountReadPossibleCapacitiesDone:
  1496.         {
  1497.             if( (ourPB->sense[3] >= 0x08) && (ourPB->sense[8] & 0x01 == 0x01))
  1498.             {
  1499.                 // this is the maximum format for this cartridge
  1500.                 drive->capacity = *((UInt32 *) &ourPB->sense[4]);
  1501.                 drive->blockSize = *((UInt16 *) &ourPB->sense[10]);
  1502.  
  1503.                 if(    drive->capacity >0x0C00)
  1504.                 {
  1505.                     ourPB->isFloppy = false;
  1506.                 }
  1507.                 else
  1508.                 {
  1509.                     ourPB->isFloppy = true;
  1510.                 }
  1511.                 
  1512.                 // Check to see if media is write protected
  1513.                 ourPB->currentExecutionState = kMountCheckWriteProtectDoneState;
  1514.                 err = CheckWriteProtect(ourPB, &MountTheCartridge);
  1515.                 break;
  1516.             }
  1517.  
  1518.             // An error occurred.  It could be:
  1519.             //        1. we didn't get a complete descriptor
  1520.             //         2. the drive doesn't recognize this media type 
  1521.             // Reset and try again. ( Should we possibly eject the media instead??)
  1522.             ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1523.             oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1524.             err = SetInterruptTimer( &oneSecondWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1525.         }
  1526.         break;
  1527.         
  1528.         case kMountCheckWriteProtectDoneState:
  1529.         {
  1530.             if ( ( ourPB->sense[3] & 0x80 ) != 0 )
  1531.             {
  1532.                 ourPB->isWriteProtected = true;
  1533.             }
  1534.             else
  1535.             {
  1536.                 ourPB->isWriteProtected = false;
  1537.             }
  1538.             
  1539.             ourPB->currentExecutionState = kMountPreventRemovalDoneState;
  1540.             err = PreventAllowRemoval(ourPB, true, &MountTheCartridge);
  1541.         }
  1542.         break;
  1543.         
  1544.         case kMountPreventRemovalDoneState:
  1545.         {
  1546.             // The device is now mounted, and the Media is locked in place.
  1547.             // There is nothing left for us to do, so just break.
  1548.             if (InstallDrive( drive, true ) == false)
  1549.             {
  1550.                 gTheParamBlock.currentExecutionState = kEjectStartState;
  1551.                  EjectTheCartridge( &gTheParamBlock );
  1552.             }
  1553.         }
  1554.         break;
  1555.     }
  1556. }
  1557.  
  1558. void EjectTheCartridge( void *theCurrentPB )
  1559. {
  1560.     OSStatus         err = noErr;
  1561.     OurParamBlock    *ourPB;
  1562.  
  1563.     ourPB = ((OurParamBlock *) theCurrentPB);
  1564.     switch (ourPB->currentExecutionState )
  1565.     {
  1566.         case kEjectStartState:
  1567.         {
  1568.             ourPB->currentExecutionState = kEjectAllowRemovalDone;
  1569.             err = PreventAllowRemoval(ourPB, false, &EjectTheCartridge); // Allow for Ejects
  1570.         }
  1571.         break;
  1572.         
  1573.         case kEjectAllowRemovalDone:
  1574.         {
  1575.             ourPB->currentExecutionState = kEjectCartridgeDone;
  1576.             EjectCartridge(&gTheParamBlock, EjectTheCartridge);
  1577.         }
  1578.         break;
  1579.         
  1580.         case kEjectCartridgeDone:
  1581.         {
  1582.             AbsoluteTime            theWait;
  1583.             StorageExecuteCommandPB    *commandPB;
  1584.             DriveRec                *drive;
  1585.         
  1586.             drive = &ourPB->theDrive;
  1587.             RemoveDrive(drive);
  1588.             commandPB = &ourPB->drivePB.executePB;                          // use a pointer to the executePB field
  1589.             ourPB->drivePB.status = commandPB->status;
  1590.             if(commandPB->status == noErr)
  1591.             {
  1592.                 ourPB->currentExecutionState = kMountStartState;    // reset the machine
  1593.                 theWait = DurationToAbsolute(durationSecond);
  1594.                 theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1595.                 SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  1596.             }
  1597.             
  1598.             drive->capacity = 0;
  1599.             drive->blockSize = 0;
  1600.             ourPB->isFloppy = false;
  1601.             ourPB->diskInDrive = false;
  1602.         }
  1603.         break;
  1604.     }
  1605. }
  1606.  
  1607. OSStatus FormatCompletionInterrupt( void *p1, void *p2)
  1608. {
  1609. #pragma unused ( p1, p2 )
  1610.     gInterruptTimer = 0;
  1611.     gTheParamBlock.currentExecutionState = kFormatWaitDoneState;
  1612.     FormatTheCartridge( &gTheParamBlock );
  1613.     return noErr;
  1614. }
  1615.  
  1616. void FormatTheCartridge( void *theCurrentPB )
  1617. {
  1618.     OSStatus         err = noErr;
  1619.     AbsoluteTime    oneSecondWait;
  1620.     DriveRec        *drive;
  1621.     OurParamBlock    *ourPB;
  1622.  
  1623.     ourPB = ((OurParamBlock *) theCurrentPB);
  1624.     drive = &ourPB->theDrive;
  1625.     oneSecondWait = DurationToAbsolute(durationSecond);
  1626.  
  1627.     switch (ourPB->currentExecutionState )
  1628.     {
  1629.         case kFormatStartState:
  1630.         {
  1631.             ourPB->currentExecutionState = kFormatDoneState;
  1632.             err = FormatFloppyCartridge( ourPB, &FormatTheCartridge);
  1633.         }
  1634.         break;
  1635.         
  1636.         case kFormatDoneState:
  1637.         {
  1638.             AbsoluteTime    theWait;
  1639.             
  1640.             theWait = DurationToAbsolute(durationSecond*35);
  1641.             theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  1642.             SetInterruptTimer( &theWait, &FormatCompletionInterrupt, nil, &gInterruptTimer);
  1643.             err = 1;
  1644.         }
  1645.         break;
  1646.         
  1647.         case kFormatWaitDoneState:
  1648.         {
  1649.             ourPB->currentExecutionState = kFormatRequestSenseDoneState;
  1650.             ourPB->drivePB.retryCount = 0;
  1651.             err = RequestSense( ourPB, &FormatTheCartridge);
  1652.         }
  1653.         break;
  1654.         
  1655.         case kFormatRequestSenseDoneState:
  1656.         {
  1657.             //SysDebugStr("\pRequest is done");
  1658.             if(ourPB->drivePB.executePB.status == noErr)
  1659.             {
  1660.                 if(ourPB->sense[12] == 0x00)
  1661.                 {
  1662.                     ourPB->currentExecutionState = kFormatGetGeometryDoneState;
  1663.                     ourPB->getCapacity.lastLogicalBlock = 0;
  1664.                     ourPB->getCapacity.blockLength = 0;
  1665.                     err = GetMediaGeometry( ourPB, &FormatTheCartridge);
  1666.                 }
  1667.                 else
  1668.                 {
  1669.                     // The format has failed, inform the OS
  1670.                     err = controlErr;
  1671.                 }
  1672.             }
  1673.             else
  1674.             {
  1675.                 if (ourPB->drivePB.retryCount < kFormatRequestSenseRetryCount)
  1676.                 {
  1677.                     ourPB->currentExecutionState = kFormatRequestSenseDoneState;
  1678.                     ourPB->drivePB.retryCount += 1;
  1679.                     err = RequestSense( ourPB, &FormatTheCartridge);
  1680.                 }
  1681.                 else
  1682.                 {
  1683.                     // The format has failed, inform the OS
  1684.                     ourPB->drivePB.retryCount = 0;
  1685.                     err = controlErr;
  1686.                 }
  1687.             }
  1688.         }
  1689.         break;
  1690.  
  1691.         case kFormatGetGeometryDoneState:
  1692.         {
  1693.             ReadCapacityData        *getGeometry;
  1694.             
  1695.             getGeometry = (ReadCapacityData *) &ourPB->getCapacity;
  1696.  
  1697.             drive->capacity = getGeometry->lastLogicalBlock+1; // Be sure to add the zero block in
  1698.             drive->blockSize = getGeometry->blockLength;
  1699.             
  1700.             if( (ourPB->drivePB.executePB.status != noErr) || (drive->capacity == 0) || (drive->blockSize == 0))
  1701.             {
  1702.                 // The format has failed, inform the OS
  1703.                 err = controlErr;
  1704.                 break;
  1705.             }
  1706.  
  1707.             if(    drive->capacity >0x0C00)
  1708.             {
  1709.                 ourPB->isFloppy = false;
  1710.             }
  1711.             else
  1712.             {
  1713.                 ourPB->isFloppy = true;
  1714.             }
  1715.  
  1716.             ourPB->diskInDrive = true;
  1717.         }
  1718.         break;
  1719.     }
  1720.     
  1721.     ourPB->drivePB.status = err;
  1722.     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  1723. }
  1724.  
  1725.  
  1726. OSStatus ReinsertionSecondaryInterrupt( void *p1, void *p2)
  1727. {
  1728. #pragma unused ( p1, p2 )
  1729.     gInterruptTimer = 0;
  1730.     
  1731.     gTheParamBlock.currentExecutionState = kReinsertionCheckStart;
  1732.     CheckForCartridgeReinsertion( &gTheParamBlock );
  1733.     
  1734.     return noErr;
  1735. }
  1736.  
  1737. void CheckForCartridgeReinsertion( void *theCurrentPB )
  1738. {
  1739.     OSStatus         err = noErr;
  1740.     AbsoluteTime    oneSecondWait;
  1741.     DriveRec        *drive;
  1742.     OurParamBlock    *ourPB;
  1743.  
  1744.     ourPB = ((OurParamBlock *) theCurrentPB);
  1745.     drive = &ourPB->theDrive;
  1746.     oneSecondWait = DurationToAbsolute(durationSecond);
  1747.  
  1748.     switch (ourPB->currentExecutionState )
  1749.     {
  1750.         case kReinsertionCheckStart:
  1751.         {
  1752.             ourPB->currentExecutionState = kReinsertionTURDoneState;
  1753.             err = TUR( ourPB, &CheckForCartridgeReinsertion);
  1754.         }
  1755.         break;
  1756.         
  1757.         case kReinsertionTURDoneState:
  1758.         {
  1759.             ourPB->currentExecutionState = kReinsertionRequestSenseDone;
  1760.             err = RequestSense( ourPB, &CheckForCartridgeReinsertion);
  1761.         }
  1762.         break;
  1763.         
  1764.         case kReinsertionRequestSenseDone:
  1765.         {
  1766.             if(ourPB->sense[12] == 0x00)
  1767.             {
  1768.                 ourPB->diskInDrive = true;
  1769.             }
  1770.             else
  1771.             {
  1772.                 ourPB->currentExecutionState = kReinsertionCheckStart;
  1773.                 oneSecondWait = AddAbsoluteToAbsolute(UpTime(), oneSecondWait);
  1774.                 err = SetInterruptTimer( &oneSecondWait, &ReinsertionSecondaryInterrupt, nil, &gInterruptTimer);
  1775.                 err = noErr;
  1776.             }
  1777.         }
  1778.         break;
  1779.     }
  1780. }
  1781.  
  1782.  
  1783. OSStatus CheckWriteProtect(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1784. {
  1785.     OSStatus    status = noErr;
  1786.     
  1787.     IfDebugging("\pCheck Write Protect");
  1788.  
  1789.     if (gItsTheDispatchTable)
  1790.     {
  1791.         StorageExecuteCommandPB    *commandPB;
  1792.         
  1793.         commandPB = &theCurrentPB->drivePB.executePB;                      // use a pointer to the executePB field
  1794.         
  1795.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));            // clear out the PB we will send
  1796.         BlockZero((Ptr) &theCurrentPB->sense[0], 8);
  1797.  
  1798.         commandPB->cdb[0] =            kCmdModeSense;
  1799.         commandPB->cdb[8] =            0x08;
  1800.         commandPB->flags =             kStorageDataIn;                                    // -> Expect a data in transfer
  1801.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];                    // -> Pointer to user buffer
  1802.         commandPB->expectedCount =    8;                                                // -> Expected number of bytes to transfer
  1803.         commandPB->completionProc =    ourCompletion;                                    // -> Completion routine
  1804.         
  1805.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1806.     }
  1807.     
  1808.     return status;
  1809. }
  1810.  
  1811. OSStatus TUR(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1812. {
  1813.     OSStatus    status = noErr;
  1814.     
  1815.     IfDebugging("\pTUR");
  1816.  
  1817.     if (gItsTheDispatchTable)
  1818.     {
  1819.         StorageExecuteCommandPB    *commandPB;
  1820.         
  1821.         commandPB = &theCurrentPB->drivePB.executePB;              // use a pointer to the executePB field
  1822.         
  1823.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));    // clear out the PB we will send
  1824.     
  1825.         commandPB->flags =             kStorageNoData;    
  1826.         commandPB->completionProc =    ourCompletion;
  1827.         
  1828.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1829.     }
  1830.     
  1831.     return status;
  1832. }
  1833.  
  1834. OSStatus RequestSense(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1835. {
  1836.     OSStatus                status = noErr;
  1837.  
  1838.     if (gItsTheDispatchTable)
  1839.     {
  1840.         StorageExecuteCommandPB    *commandPB;
  1841.         
  1842.         commandPB = (StorageExecuteCommandPB *)&theCurrentPB->drivePB.executePB;              // use a pointer to the executePB field
  1843.  
  1844.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1845.         BlockZero((Ptr) &theCurrentPB->sense[0], sizeof(kSenseDataSize));
  1846.         
  1847.         commandPB->cdb[0] =            kCmdRequestSense;
  1848.         commandPB->cdb[4] =            kSenseDataSize;
  1849.         commandPB->flags =             kStorageDataIn;                            // -> Expect a data in transfer
  1850.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];            // -> Pointer to user buffer
  1851.         commandPB->expectedCount =    kSenseDataSize;                            // -> Expected number of bytes to transfer
  1852.         commandPB->completionProc =    ourCompletion;                            // -> Completion routine
  1853.         
  1854.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1855.     }
  1856.     
  1857.     return status;
  1858. }
  1859.  
  1860. OSStatus GetMediaGeometry(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1861. {
  1862.     OSStatus    status = noErr;
  1863.  
  1864.     IfDebugging("\pGetMediaGeometry");
  1865.  
  1866.     if (gItsTheDispatchTable)
  1867.     {
  1868.         ReadCapacityData        *getGeometry;
  1869.         StorageExecuteCommandPB    *commandPB;
  1870.         
  1871.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1872.         getGeometry = &theCurrentPB->getCapacity;
  1873.         
  1874.         getGeometry->lastLogicalBlock = 0;
  1875.         getGeometry->blockLength = 0;
  1876.         
  1877.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1878.  
  1879.         commandPB->cdb[0] =            kCmdReadCapacity;
  1880.         commandPB->flags =             kStorageDataIn;                            // -> Expect a data in transfer
  1881.         commandPB->userBuffer =        (Ptr) getGeometry;                        // -> Pointer to user buffer
  1882.         commandPB->expectedCount =    sizeof(ReadCapacityData);                // -> Expected number of bytes to transfer
  1883.         commandPB->completionProc =    ourCompletion;                            // -> Completion routine
  1884.         
  1885.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1886.     }
  1887.  
  1888.     return status;
  1889. }
  1890.  
  1891.  
  1892. OSStatus ReadFormatCapacity(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1893. {
  1894.     OSStatus    status = noErr;
  1895.  
  1896.     IfDebugging("\pReadFormatCapacity");
  1897.  
  1898.     if (gItsTheDispatchTable)
  1899.     {
  1900.         StorageExecuteCommandPB    *commandPB;
  1901.         
  1902.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1903.  
  1904.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1905.  
  1906.         commandPB->cdb[0] =            kCmdReadFormatCapacities;
  1907.         commandPB->cdb[7] =            0;
  1908.         commandPB->cdb[8] =            0x0C;
  1909.         commandPB->flags =             kStorageDataIn;                                    // -> Expect a data in transfer
  1910.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];                    // -> Pointer to user buffer
  1911.         commandPB->expectedCount =    0x0C;                                            // -> Expected number of bytes to transfer
  1912.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1913.         
  1914.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1915.     }
  1916.  
  1917.     return status;
  1918. }
  1919.  
  1920.  
  1921. OSStatus FormatFloppyCartridge(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1922. {
  1923.     OSStatus    status = noErr;
  1924.  
  1925.     IfDebugging("\pFormatFloppyCartridge");
  1926.  
  1927.     if (gItsTheDispatchTable)
  1928.     {
  1929.         StorageExecuteCommandPB    *commandPB;
  1930.  
  1931.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1932.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1933.         BlockZero((Ptr) &theCurrentPB->sense[0], 0x0C);
  1934.  
  1935.         // Set up the data going out
  1936.         // First set up the Defect List Header
  1937.         theCurrentPB->sense[0] = 0;
  1938.         //theCurrentPB->sense[1] = 0x82;
  1939.         theCurrentPB->sense[1] = 0;
  1940.         theCurrentPB->sense[2] = 0;
  1941.         theCurrentPB->sense[3] = 0x08;
  1942.  
  1943.         *((UInt32 *) &theCurrentPB->sense[4]) = theCurrentPB->theDrive.capacity;
  1944.         *((UInt16 *) &theCurrentPB->sense[10]) = theCurrentPB->theDrive.blockSize;
  1945.         
  1946.         commandPB->cdb[0] =            kCmdFormat;
  1947.         commandPB->cdb[1] =            0x17;
  1948.         commandPB->flags =             kStorageDataOut;                    // -> Expect a data out transfer
  1949.         commandPB->userBuffer =        (Ptr) &theCurrentPB->sense[0];        // -> Pointer to user buffer
  1950.         commandPB->expectedCount =    0x0C;                                // -> Expected number of bytes to transfer
  1951.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1952. #if 0
  1953.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1954.  
  1955.         commandPB->cdb[0] =            0x24;
  1956.         commandPB->cdb[6] =            (((theCurrentPB->theDrive.blockSize >> 8) & 0x07) << 4) | 8;
  1957.         commandPB->flags =             kStorageNoData;                    // -> Expect a data out transfer
  1958.         commandPB->userBuffer =        nil;                            // -> Pointer to user buffer
  1959.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1960. #endif
  1961.  
  1962.         //SysDebugStr("\pBreak for format");
  1963.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1964.     }
  1965.  
  1966.     return status;
  1967. }
  1968.  
  1969.  
  1970. OSStatus EjectCartridge(OurParamBlock *theCurrentPB, StorageClassCompletionProcPtr ourCompletion)
  1971. {
  1972.     OSStatus    status = noErr;
  1973.  
  1974.     if (gItsTheDispatchTable)
  1975.     {
  1976.         StorageExecuteCommandPB    *commandPB;
  1977.         
  1978.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  1979.  
  1980.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  1981.         
  1982.         commandPB->cdb[0] =            kCmdStartStopUnit;
  1983.         commandPB->cdb[4] =            0x02;                            // Unload the Media
  1984.         commandPB->flags =             kStorageNoData;                    // -> Expect a data in transfer
  1985.         commandPB->userBuffer =        nil;                            // -> Pointer to user buffer
  1986.         commandPB->expectedCount =    0;                                // -> Expected number of bytes to transfer
  1987.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  1988.         
  1989.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  1990.     }
  1991.  
  1992.     return status;
  1993. }
  1994.  
  1995.  
  1996. OSStatus PreventAllowRemoval(OurParamBlock *theCurrentPB, Boolean preventRemoval, StorageClassCompletionProcPtr ourCompletion)
  1997. {
  1998.     OSStatus    status = noErr;
  1999.  
  2000.     if (gItsTheDispatchTable)
  2001.     {
  2002.         StorageExecuteCommandPB    *commandPB;
  2003.         
  2004.         commandPB = &theCurrentPB->drivePB.executePB;                          // use a pointer to the executePB field
  2005.  
  2006.         BlockZero(commandPB, sizeof(StorageExecuteCommandPB));
  2007.         
  2008.         commandPB->cdb[0] =            kCmdPreventAllowRemoval;
  2009.         if ( preventRemoval == true )
  2010.         {
  2011.             commandPB->cdb[4] =            0x01;                        // Prevent Media Removal
  2012.         }
  2013.         else
  2014.         {
  2015.             commandPB->cdb[4] =            0x00;                        // Allow Media Removal
  2016.         }
  2017.         
  2018.         commandPB->flags =             kStorageNoData;                    // -> Expect a data in transfer
  2019.         commandPB->userBuffer =        nil;                            // -> Pointer to user buffer
  2020.         commandPB->expectedCount =    0;                                // -> Expected number of bytes to transfer
  2021.         commandPB->completionProc =    (StorageClassCompletionProcPtr) ourCompletion;    // -> Completion routine
  2022.         
  2023.         status = (gItsTheDispatchTable->pStorageExecuteCmd)(commandPB);
  2024.     }
  2025.  
  2026.     return status;
  2027. }
  2028.  
  2029.  
  2030. /* ---------------------Supporting Functions below this point -------------------------- */
  2031. //------------------------------------------------------------------------------
  2032. //    Function:        DRVRPrime
  2033. //    Description:    This is the ATA driver PRIME call that performs
  2034. //                    reading and writing to the device.
  2035. //                    
  2036. //    Input:            ioPB = Pointer to caller's I/O parameter block
  2037. //                    dce = Pointer to Device Control Entry (DCE)
  2038. //    Output:            A status code is returned.
  2039. //-------------------------------------------------------------------------------
  2040. OSStatus DoReadWriteCommand( OurParamBlock *theDriverPB, Boolean doWrite )
  2041. {
  2042.     OSStatus        err;
  2043.     UInt32            startingBlock, numBlocks;
  2044.     VolumeRecPtr    vol;
  2045.     IOParamPtr         iopb;
  2046.     DriveRecPtr     drive;
  2047.     
  2048.     drive = &theDriverPB->theDrive;
  2049.     iopb = (IOParamPtr) theDriverPB->drivePB.theIOPB;
  2050.  
  2051.     // Find the associated volume and drive records required for the request.
  2052.     vol = GetVolume(drive, iopb->ioVRefNum, 0);            // Get the volume record requested 
  2053.     
  2054.     //if (vol)                                        // if we have a volume
  2055.     //    drive = (DriveRecPtr)vol->drivePtr;            // get its drive record
  2056.     //else                // assume request if for a physical drive
  2057.     if (!vol)                                        // if we have a volume
  2058.     {
  2059.         if (drive)                                    // if physical drive matches…
  2060.         {
  2061.             vol = drive->nextVol;
  2062.             if (vol)                                // and a volume exists for it…
  2063.             {
  2064.                 if (vol->curoffset)                    // and not doing physical addressing…
  2065.                 {
  2066.                     vol = 0;                        // the volume is invalid
  2067.                 }
  2068.             }
  2069.         }
  2070.     }
  2071.     
  2072.     if (!vol || !drive)                // Abort if we don't have both drive and volume records
  2073.         return(nsDrvErr);
  2074.         
  2075.     if (drive->busState != kONLINE)    // drive must be connected also!
  2076.         return(nsDrvErr);
  2077.  
  2078.     //................................................................................
  2079.     // A usable drive and volume exists.  Continue processing the request…
  2080.  
  2081.     // Compute the starting block address for the request.  We accept
  2082.     // only the normal 32 bit address and not the new 'Large Volume (64 bit) Addressing'.
  2083.     //SysDebugStr("\pCalculate the Read.");
  2084.     startingBlock = (UInt32)((iopb->ioPosOffset)/(drive->blockSize));
  2085.     numBlocks = (iopb->ioReqCount)/(drive->blockSize);
  2086.  
  2087.     if (doWrite && vol->driveStatus.writeProt)
  2088.     {
  2089.         // check for write protect
  2090.         err = wPrErr;
  2091.     }
  2092.     else if (iopb->ioReqCount & ((drive->blockSize) - 1))
  2093.     {
  2094.         // Verify if request is a multiple of the drives blocksize
  2095.         err = paramErr;
  2096.     }
  2097.     else if ( ( startingBlock + numBlocks ) > ((vol->curoffset) ? vol->partblks : drive->capacity))
  2098.     {
  2099.         // Verify if request is within range with respect to doing physical or logical I/O.
  2100.         // Access is limited to partition range (logical I/O) if curoffset is non-zero.
  2101.         err = paramErr;
  2102.     }
  2103.     else if ((iopb->ioPosMode & rdVerify) && !doWrite)
  2104.     {
  2105.         // If Read-Verify mode, this is not efficient with disks so we simply pretend we did it.
  2106.         iopb->ioActCount = iopb->ioReqCount;
  2107.         iopb->ioPosOffset += iopb->ioActCount;
  2108.         err = noErr;
  2109.     } 
  2110.     else                                    // Do the read or write
  2111.     {
  2112.         startingBlock += vol->curoffset;            // add in the partition offset
  2113.         err = ReadWriteBlock( theDriverPB, startingBlock, numBlocks, iopb->ioBuffer, doWrite, true );
  2114.     }
  2115.  
  2116.     return err;
  2117. }
  2118.  
  2119. //------------------------------------------------------------------------------
  2120. //    Function:        ReadWriteBlock
  2121. //
  2122. //    Description:    Low level read/write block on the media with retries.
  2123. //
  2124. //    Input:            drive:            Pointer to physical drive record
  2125. //                        blockAddr:        Starting block address 
  2126. //                        numBlocks:        Number of blocks to read/write
  2127. //                        buffer:            Pointer to buffer
  2128. //                        doWrite:            1 = write, 0 = read
  2129. //
  2130. //    Output:            true if successful, false if not
  2131. //-------------------------------------------------------------------------------
  2132. OSStatus ReadWriteBlock( OurParamBlock *theDriverPB, UInt32 startBlock, UInt32 numBlocks, Ptr buffer, Boolean doWrite, Boolean doAsync)
  2133. {    
  2134.     StorageExecuteCommandPBPtr     theReadWriteRequest;
  2135.     UInt32                        driveBlockSize;
  2136.     volatile OSStatus            status = noErr;
  2137.  
  2138.     IfDebugging("\p…ReadWriteBlock");
  2139.     theDriverPB->drivePB.status = noErr;
  2140.     driveBlockSize = theDriverPB->theDrive.blockSize;
  2141.     theReadWriteRequest = &(theDriverPB->drivePB.executePB);
  2142.  
  2143.     BlockZero(theReadWriteRequest, sizeof(StorageExecuteCommandPB));    // clear out the PB we will send
  2144.     theReadWriteRequest->userBuffer = buffer;                // -> Pointer to user buffer
  2145.     theReadWriteRequest->expectedCount = numBlocks*driveBlockSize;    // -> Expected number of bytes to transfer
  2146.  
  2147.     theReadWriteRequest->completionProc = (StorageClassCompletionProcPtr) ReadWriteCompletion;    // -> Completion routine
  2148.     theReadWriteRequest->actualCount = 0;                    // <- Actual number of bytes transferred
  2149.     theReadWriteRequest->status = 0;                    // <- Result of operation
  2150.  
  2151.     if(doWrite)
  2152.     {
  2153.         theReadWriteRequest->cdb[0] = kCmdWrite;
  2154.         theReadWriteRequest->flags     = kStorageDataOut;        // -> Expect a data out transfer
  2155.     }
  2156.     else
  2157.     {
  2158.         theReadWriteRequest->cdb[0] = kCmdRead;
  2159.         theReadWriteRequest->flags     = kStorageDataIn;        // -> Expect a data in transfer
  2160.     }
  2161.     
  2162.     // Set the starting block in the CDB
  2163.     theReadWriteRequest->cdb[2] = (startBlock >> 24) & 0xff;
  2164.     theReadWriteRequest->cdb[3] = (startBlock >> 16) & 0xff;
  2165.     theReadWriteRequest->cdb[4] = (startBlock >> 8) & 0xff;
  2166.     theReadWriteRequest->cdb[5] = startBlock & 0xff;
  2167.     
  2168.     // Set the Block Count in the CDB
  2169.     theReadWriteRequest->cdb[7] = (numBlocks >> 8) & 0xff;
  2170.     theReadWriteRequest->cdb[8] = numBlocks & 0xff;
  2171.  
  2172.     status = ((gItsTheDispatchTable)->pStorageExecuteCmd)( theReadWriteRequest );            
  2173.  
  2174.     theDriverPB->drivePB.status = status;
  2175.  
  2176.     if(status != 1)
  2177.     {
  2178.         theDriverPB->drivePB.theIOPB = nil;
  2179.     }
  2180.     else
  2181.     {
  2182.         if(doAsync == false)
  2183.         {
  2184.             while ( status == 1 )
  2185.             {
  2186.                 status = theDriverPB->drivePB.status;
  2187.             }
  2188.         }
  2189.     }
  2190.     
  2191.     return status;
  2192. }
  2193.  
  2194.  
  2195. void ReadWriteCompletion( void *theDriverPB )
  2196. {
  2197.     OSStatus            status;
  2198.     OurParamBlock        *ourPB;
  2199.     Boolean             wasWrite;
  2200.     
  2201.     //SysDebugStr("\pReadWriteCompletion;g");
  2202.     ourPB = (OurParamBlock *) theDriverPB;
  2203.     status = ourPB->drivePB.executePB.status;
  2204.     wasWrite = ((ourPB->drivePB.executePB.flags & kStorageDataOut) == kStorageDataOut);
  2205.     if(ourPB->doInternalReadWrite == false)
  2206.     {
  2207.         IOParamPtr         iopb;
  2208.  
  2209.         iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2210.         
  2211.         if( status == noErr )
  2212.         {
  2213.             if( wasWrite == true )
  2214.             {
  2215.                 // It was a write, do a Request sense to determine if any 
  2216.                 // errors occurred.
  2217.                 status = RequestSense( ourPB, &WriteRequestSenseCompletion);
  2218.                 
  2219.                 if( status != 1)
  2220.                 {
  2221.                     // An error occurred while trying to do the Request sense,
  2222.                     // return an ioErr to the system.
  2223.                     iopb->ioActCount = 0;
  2224.                     
  2225.                     // Set the status in the DriverPB last, this way if there is an immediate command,
  2226.                     // It won't think the command is done till after our processing.
  2227.                     ourPB->drivePB.status = ioErr;
  2228.                     
  2229.                     // Signal completion of the command to the operating system
  2230.                     ourPB->drivePB.theIOPB = nil;
  2231.                     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2232.                 }
  2233.             }
  2234.             else
  2235.             {
  2236.                 // This was a read command, and no errors occurred, finish the IO request and
  2237.                 // return to the system.
  2238.                 iopb->ioActCount = iopb->ioReqCount;
  2239.                 
  2240.                 // Set the status in the DriverPB last, this way if there is an immediate command,
  2241.                 // It won't think the command is done till after our processing.
  2242.                 ourPB->drivePB.status = noErr;
  2243.                 
  2244.                 // Signal completion of the command to the operating system
  2245.                 ourPB->drivePB.theIOPB = nil;
  2246.                 FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2247.             }
  2248.         }
  2249.         else
  2250.         {
  2251.             // An error occurred on the command, do a Request sense to be certain any
  2252.             // USB Device stalls are cleared.
  2253.             status = RequestSense( ourPB, &RequestSenseOnErrorCompletion);
  2254.             
  2255.             if( status != 1)
  2256.             {
  2257.                 // An error occurred while trying to do the Request sense,
  2258.                 // return an ioErr to the system.
  2259.                 iopb->ioActCount = 0;
  2260.                 
  2261.                 // Set the status in the DriverPB last, this way if there is an immediate command,
  2262.                 // It won't think the command is done till after our processing.
  2263.                 ourPB->drivePB.status = ioErr;
  2264.                 
  2265.                 // Signal completion of the command to the operating system
  2266.                 ourPB->drivePB.theIOPB = nil;
  2267.                 FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2268.             }
  2269.         }
  2270.     }
  2271.     else
  2272.     {
  2273.         // If this is an internal command, this is all we care about
  2274.         ourPB->drivePB.status = status;
  2275.     }
  2276.  
  2277.     IfDebugging("\p…ReadWriteBlock Done");
  2278. }
  2279.  
  2280. void WriteRequestSenseCompletion( void *theDriverPB )
  2281. {
  2282.     OSStatus        status;
  2283.     OurParamBlock    *ourPB;
  2284.     IOParamPtr         iopb;
  2285.     
  2286.     ourPB = (OurParamBlock *) theDriverPB;
  2287.     status = ourPB->drivePB.executePB.status;
  2288.     iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2289.  
  2290.     if( status == noErr )
  2291.     {
  2292.         if( ( ourPB->sense[2] & 0x0F !=0 ) && ( ourPB->sense[2] & 0x0F !=1 ))
  2293.         {
  2294.             // An error has been reported back in the sense key, return 
  2295.             // an ioErr to the system.
  2296.             iopb->ioActCount = 0;
  2297.             ourPB->drivePB.status = ioErr;
  2298.         }
  2299.         else
  2300.         {
  2301.             // No errors has been reported back in the sense key, return 
  2302.             // a noErr to the system.
  2303.             iopb->ioActCount = iopb->ioReqCount;
  2304.             ourPB->drivePB.status = noErr;
  2305.         }
  2306.     }
  2307.     else
  2308.     {
  2309.         // Errors occurred on the Request sense report an
  2310.         // ioErr back to the system
  2311.         iopb->ioActCount = 0;
  2312.         ourPB->drivePB.status = ioErr;
  2313.     }
  2314.  
  2315.     // Signal completion of the command to the operating system
  2316.     //SysDebugStr("\pWriteReqSenseFinishCommand");
  2317.     ourPB->drivePB.theIOPB = nil;
  2318.     FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2319. }
  2320.  
  2321. void RequestSenseOnErrorCompletion( void *theDriverPB )
  2322. {
  2323.     OurParamBlock    *ourPB;
  2324.     IOParamPtr         iopb;
  2325.     
  2326.     ourPB = (OurParamBlock *) theDriverPB;
  2327.     iopb = (IOParamPtr) ourPB->drivePB.theIOPB;
  2328.         
  2329.     // An error occurred on the initial command, and no error occurred on the RequestSense
  2330.     if( (ourPB->drivePB.retryCount < kReadWriteRetryCount) && (ourPB->drivePB.executePB.status == noErr))
  2331.     {
  2332.         OSStatus err;
  2333.         // We have not yet exceeded the retry count, so try the operation again
  2334.  
  2335.         // Check to see if the disk was manually removed (using the emergency pinhole)
  2336.         if(((ourPB->sense[2] & 0x0F) == 0x02) && (ourPB->sense[12] == 0x3A))
  2337.         {
  2338.             AbsoluteTime        theWait;
  2339.             
  2340.             //SysDebugStr("\pWhere did yo Go?");
  2341.             ourPB->diskInDrive = false;
  2342.             ourPB->theDrive.busState = kOFFLINE;
  2343.             CheckUnexpectedRemoval(&ourPB->theDrive);
  2344.             RemoveDrive(&gTheParamBlock.theDrive);
  2345.             
  2346.             theWait = DurationToAbsolute(durationSecond);
  2347.             theWait = AddAbsoluteToAbsolute(UpTime(), theWait);
  2348.             (void) SetInterruptTimer( &theWait, &MountSecondaryInterrupt, nil, &gInterruptTimer);
  2349.             err = ioErr;
  2350.         }
  2351.         else
  2352.         {
  2353.             // Increment our retry counter
  2354.             ourPB->drivePB.retryCount += 1;
  2355.     
  2356.             // Send the command out again
  2357.             err = DoReadWriteCommand( ourPB, ourPB->drivePB.doWrite);
  2358.         }
  2359.         
  2360.         if ( err !=1 )
  2361.         {
  2362.             iopb->ioActCount = 0;
  2363.             ourPB->drivePB.status = ioErr;
  2364.         
  2365.             // Signal completion of the command to the operating system
  2366.             ourPB->drivePB.theIOPB = nil;
  2367.             FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2368.         }
  2369.     }
  2370.     else
  2371.     {
  2372.         // Since we only get here if an error occurred on the orignal
  2373.         // command, and the Request Sense was to clear any device stalls.
  2374.         // We will always report back an ioErr to the system.
  2375.         iopb->ioActCount = 0;
  2376.         ourPB->drivePB.status = ioErr;
  2377.     
  2378.         // Signal completion of the command to the operating system
  2379.         ourPB->drivePB.theIOPB = nil;
  2380.         FinishCommandProcessing(ourPB->drivePB.ioCommandID, ourPB->drivePB.ioCommandKind, ourPB->drivePB.status);
  2381.     }
  2382. }
  2383.  
  2384.  
  2385. //------------------------------------------------------------------------------
  2386. //    Function:        GetVolume
  2387. //
  2388. //    Description:    Searches for the volume record with the specified vRefNum and 
  2389. //                    returns its VolumeRecPtr.  Optionally, partitionNum
  2390. //                    can be use as search keys if vRefNum is zero.
  2391. //
  2392. //    Input:            drive:            the pointer to the DriveRec
  2393. //                    vRefNum:        the volume's reference number
  2394. //                    partitionNum:    the volume's partition number
  2395. //
  2396. //    Output:            pointer to volume record if found, nil if not found
  2397. //-------------------------------------------------------------------------------
  2398. VolumeRecPtr GetVolume(DriveRecPtr drive, UInt16 vRefNum, UInt32 partitionNum)
  2399. {
  2400.     VolumeRecPtr vol = nil;
  2401.     Boolean found;
  2402.     
  2403.     // Search all volume records for one matching the search key
  2404.  
  2405.     vol = drive->nextVol;                        // first volume pointer
  2406.  
  2407.     while (vol)
  2408.     {
  2409.         found = (vRefNum) ? (vol->vRefNum == vRefNum) : (vol->partitionNo == partitionNum);
  2410.  
  2411.         if (found)
  2412.             return(vol);                        // found the partition
  2413.         else
  2414.             vol = (VolumeRecPtr)vol->nextVol;    // otherwise, get next volume pointer
  2415.     }
  2416.     
  2417.     return(vol);                                // nil if volume not found
  2418. }
  2419.  
  2420.  
  2421. //------------------------------------------------------------------------------
  2422. //    Function:        CreateVolume
  2423. //
  2424. //    Description:    This function creates a volume record for the specified drive.
  2425. //                        The volume is appended to the drive's volume queue and a logical
  2426. //                        logical drive is installed in the system drive queue.  It is 
  2427. //                        assumed the volume is not write protected, drive is installed,
  2428. //                        and the media is installed.  
  2429. //                    
  2430. //    Input:            drive:            pointer to the drive record of the volume to create
  2431. //                    partitionID:    the volume's partition ID
  2432. //                    volSize:        size of the volume in blocks
  2433. //                    volOffset:        block offset of volume on drive
  2434. //
  2435. //    Output:            Returns nil pointer if fails, else pointer to volume record
  2436. //
  2437. //    NOTE:            Assumes all inputs are valid!
  2438. //-------------------------------------------------------------------------------
  2439. VolumeRecPtr CreateVolume(DriveRecPtr drive, UInt32 partitionID, UInt32 volSize, UInt32 volOffset )
  2440. {
  2441.     register VolumeRecPtr        vol;
  2442.     VolumeRecPtr*                volHandle;
  2443.     Boolean                        volInQueue = false;
  2444.     
  2445.     // When a volume is offlined by the system (greyed desktop icon) it is still
  2446.     // being used and its associated DrvQEl must remain.  Thus, its volume and 
  2447.     // physical drive record also must remain.  So, when new media is inserted 
  2448.     // we need to check for these structures and reuse them before creating new ones.
  2449.     // The structures can be reused since the file system verifies the volume.
  2450.  
  2451.     if( drive->nextVol == nil)
  2452.     {
  2453.         // Make sure the drive's volume queue points to the volume
  2454.         vol = &gTheParamBlock.theVolume;
  2455.     }
  2456.     else
  2457.     {
  2458.         // Search for a volume record for the volume (partition) to be created…
  2459.         vol = GetVolume(drive, 0, partitionID);
  2460.         
  2461.         if (vol)                                                        // if record exists for this volume…
  2462.         {
  2463.             volInQueue = true;                                            // remember volume is already in queue
  2464.     
  2465.             if (vol->driveStatus.diskInPlace)                            // if volume is already online…
  2466.                 vol = nil;                                                // we shouldn't be here - fall thru
  2467.         }
  2468.         else                                                            // need to create volume record
  2469.         {
  2470.             vol = (VolumeRecPtr) PoolAllocateResident(sizeof(VolumeRec), true);    // Allocate storage for record
  2471.             //vol = (VolumeRecPtr) NewPtrSysClear(sizeof(VolumeRec));        // Allocate storage for record
  2472.         }
  2473.     }
  2474.     
  2475.     if (vol)                                                        // if a volume record is valid…
  2476.     {    
  2477.         if( gTheParamBlock.isFloppy == true)
  2478.         {
  2479.             vol->driveStatus.track             = 80;            // Sectors on a MFM disk
  2480.             vol->driveStatus.sides             = -1;            // -1 means double sided floppy
  2481.         }
  2482.         else
  2483.         {
  2484.             vol->driveStatus.track             = 0;            // Only used on floppys
  2485.             vol->driveStatus.sides             = 0;            // Only used on floppys
  2486.         }        
  2487.  
  2488.         if( gTheParamBlock.isWriteProtected == true)
  2489.         {
  2490.             vol->driveStatus.writeProt     = 0x80;            // disk is write protected
  2491.         }
  2492.         else
  2493.         {
  2494.             vol->driveStatus.writeProt     = 0;            // not write protected yet
  2495.         }
  2496.         vol->driveStatus.diskInPlace     = 1;            // Ejectable Disk
  2497.         vol->driveStatus.installed         = 1;            // drive is installed
  2498.         vol->driveStatus.qType             = 1;            // both dQDrvSz and dQDrvSz2 are used
  2499.         vol->driveStatus.dQFSID         = 0;            // File Manager's volume type
  2500.         vol->driveStatus.driveSize        = (UInt16) volSize;        // volume size in blocks
  2501.         vol->driveStatus.driveS1         = (UInt16) (volSize >> 16);
  2502.  
  2503.         vol->mountthispart =    false;                                // don't mount this volume,
  2504.         vol->partmounted =        false;                                // it's not mounted yet,
  2505.         vol->partitionNo =        partitionID;                        // save the partition ID
  2506.  
  2507.         // Save volume's block offset and set its access mode by setting curoffset to the
  2508.         // same (access is relative to partition offset if curoffset is non-zero, else physical)
  2509.         vol->curoffset =        volOffset;                            // block offset of volume
  2510.         vol->partoffset =        volOffset;                            // partition offset
  2511.         vol->partblks =            volSize;                            // save size for our use also
  2512.         vol->drivePtr =        (Ptr) drive;                            // pointer to vol's physical drive
  2513.         if( gTheParamBlock.isFloppy == true)
  2514.         {
  2515.             DrvSts    *theDriveStatus;
  2516.             
  2517.             theDriveStatus = (DrvSts *) &vol->driveStatus;
  2518.             theDriveStatus->twoSideFmt = -1;                /* after 1st rd/wrt: 0=1 side, -1=2 side */
  2519.             theDriveStatus->needsFlush = -1;                /* -1 for MacPlus drive */
  2520.             theDriveStatus->diskErrs = 0;                    /* soft error count */
  2521.  
  2522.             vol->mediaIconPtr =    (Ptr) AppleFloppyMediaIcon;        // media icon for floppies
  2523.         }
  2524.         else
  2525.         {
  2526.             vol->mediaIconPtr =    (Ptr) &CartridgeIcon;                    // media icon for cartridge
  2527.         }
  2528.             
  2529.         // If this record is not in our volume list yet, finish initializing and insert
  2530.         if (!volInQueue)                                            // If not in the volume queue…
  2531.         {
  2532.             vol->driveStatus.qLink = nil;                            // initialize system queue link
  2533.             vol->nextVol = nil;                                        // no link to next volume yet
  2534.  
  2535.             // Find the end of the drive's volume queue and link in the new volume record.
  2536.             volHandle = &(drive->nextVol);                            // point to start of drive's volume queue
  2537.             while (*volHandle)                                        // search for end of volume queue…
  2538.             {
  2539.                 volHandle = (VolumeRecPtr*)&((*volHandle)->nextVol);
  2540.             }
  2541.             
  2542.             *volHandle = vol;                                        // at end of queue, insert volume
  2543.         }
  2544.  
  2545.         drive->numVolumes++;                                        // update number of drive volumes
  2546.     }
  2547.     return(vol);
  2548. }
  2549.  
  2550.  
  2551. //------------------------------------------------------------------------------
  2552. //    Function:        InstallVolumes
  2553. //    Description:    Searches for partitions on the media and installs them as
  2554. //                        volumes for the associated drive.  Assumes the drive does
  2555. //                        not have any volumes installed yet. 
  2556. //
  2557. //    Input:            theDrive:    pointer to drive record
  2558. //                        mountVols:    true means to mount the drive's partitions
  2559. //
  2560. //-------------------------------------------------------------------------------
  2561. void InstallVolumes(DriveRecPtr theDrive, Boolean mountVols)
  2562. {
  2563.     VolumeRecPtr    vol = nil;
  2564.  
  2565.     // If no partitions were found from the search above we assume the following:
  2566.     //        1. A Macintosh partition map exists, but it has either no file system partitions
  2567.     //            or no partitions which we recognize (either or which is unlikely).
  2568.     //        2.    The media has a Macintosh floppy format (HFS with no partition map)
  2569.     //        3.    The media has a foreign format (DOS, etc.).
  2570.     //
  2571.     // In all cases we create a volume of the entire media capacity, post a disk inserted
  2572.     // event and let the File System Manager try and figure it out.  Note we post a disk
  2573.     // inserted event rather than notifying FSM so if the media is not recognized (because
  2574.     // it's unformatted or the correct file system is not installed) the system will prompt
  2575.     // with a "This is not a Macintosh disk…" message.  If we call FSM instead, the user will
  2576.     // not be prompted when the media is unformatted.  This provides a way to format media
  2577.     // and also allows us to eject PCMCIA drives which have no volumes (no icons on desktop).
  2578.  
  2579.     if (theDrive->numVolumes == 0)                                    // if no partitions were found…
  2580.     {
  2581.         IfDebugging("\ptheDrive->numVolumes == 0");
  2582.         vol = CreateVolume(theDrive, 1, theDrive->capacity, 0);
  2583.  
  2584.         if (vol)
  2585.             vol->mountthispart = true;                                    // post disk inserted event later
  2586.     }
  2587.  
  2588.     // Add the remaining volumes to the drive queue…
  2589.     vol = theDrive->nextVol;                                            // first volume of the drive
  2590.     while (vol)
  2591.     {
  2592.         if ((vol->mountthispart == true) || (mountVols == false))
  2593.         {
  2594.             UInt16 volNumber;
  2595.             
  2596.             vol->vRefNum = NextQDrive();        // assign a logical drive number
  2597.             volNumber = vol->vRefNum;
  2598.             
  2599.             // Check to see if we need to add one to the AddDrive call
  2600.             if( gTheParamBlock.addOneToAddDrive == true)
  2601.             {
  2602.                 volNumber += 1;
  2603.             }
  2604.             
  2605.             AddDrive(gTheParamBlock.drvrRefNum, volNumber,(DrvQElPtr)  &vol->driveStatus.qLink);
  2606.         }
  2607.         vol = (VolumeRecPtr)vol->nextVol;                            // point to the next volume
  2608.     }
  2609. }
  2610.  
  2611.  
  2612. //------------------------------------------------------------------------------
  2613. //    Function:        RemoveVolume
  2614. //    Description:    Deletes a volume and its record
  2615. //    Input:            drvRec:        pointer to physical drive record of volume
  2616. //                    volRef:        Volume reference
  2617. //-------------------------------------------------------------------------------
  2618. void RemoveVolume(DriveRecPtr drvRec, UInt16 volRef)
  2619. {
  2620.     VolumeRecPtr*    pvHandle = &(drvRec->nextVol);
  2621.     VolumeRecPtr    pvPtr = *pvHandle;
  2622.     
  2623.     while (pvPtr) 
  2624.     {
  2625.         if (pvPtr->vRefNum == volRef)             // found the volume record
  2626.         {        
  2627.             Dequeue((QElemPtr) &(pvPtr->driveStatus.qLink), GetDrvQHdr());    // remove from drive queue        
  2628.             *pvHandle = (VolumeRecPtr)(pvPtr->nextVol);    // remove it from the linked list
  2629.             if (pvPtr == &gTheParamBlock.theVolume)
  2630.             {
  2631.                 BlockZero((Ptr) &gTheParamBlock.theVolume, sizeof(VolumeRec));
  2632.             }
  2633.             else
  2634.             {
  2635.                 PoolDeallocate( pvPtr );            // release its memory
  2636.                 //DisposePtr((Ptr) pvPtr );            // release its memory
  2637.             }
  2638.             drvRec->numVolumes--;                // decrement number of drive volumes
  2639.             break;
  2640.         }
  2641.         
  2642.         pvHandle = (VolumeRecPtr*)&(pvPtr->nextVol);        // save pointer to pv pointer
  2643.         pvPtr = (VolumeRecPtr)pvPtr->nextVol;                // point to next pv
  2644.     }            
  2645. }
  2646.  
  2647.  
  2648. //------------------------------------------------------------------------------
  2649. //    Function:        InstallDrive
  2650. //
  2651. //    Description:    Installs a physical drive and its volumes under the driver's
  2652. //                        control.  The driver determines if the drive is one it can
  2653. //                        manage, and if so, creates and initializes the drive's record,
  2654. //                        sets the drives operating mode and options, and mounts its
  2655. //                        partitions to the system.
  2656. //
  2657. //    Input:            drvNum:        physical drive reference
  2658. //                        mountVols:    true means to mount drive's partitions
  2659. //
  2660. //    Output:            true if successful, false if not
  2661. //-------------------------------------------------------------------------------
  2662. Boolean InstallDrive(DriveRecPtr theDrive, Boolean mountVols)
  2663. {
  2664.     // Initialize variables associated with the new drive.
  2665.     
  2666.     theDrive->isSpunDown =        false;                    // drive is spinning (full power)
  2667.     theDrive->inSleepMode =        false;                    // not sleeping
  2668.     theDrive->doSpindown =        false;                    // no spindown timer yet
  2669.     theDrive->numVolumes =        0;                        // no partitions yet
  2670.     
  2671.     theDrive->busState = kONLINE;                            // drive is online
  2672.     
  2673.     //..............................................................................
  2674.     // Search for file system partitions on the media and install them as volumes
  2675.     // of this drive.  If no volumes, the drive must be considered unusable.
  2676.     InstallVolumes(theDrive, mountVols);
  2677.  
  2678.     if (theDrive->numVolumes)
  2679.     {
  2680.         OSErr theErr;
  2681.         
  2682.         theErr = MountUnmountVolumes( theDrive );
  2683.         if(theErr != noErr)
  2684.         {
  2685.             // For some reason, the disk could not be mounted,
  2686.             // We should eject and let the user decide whether to try again.
  2687.             gTheParamBlock.currentExecutionState = kEjectStartState;
  2688.             EjectTheCartridge( &gTheParamBlock );
  2689.         }
  2690.     }
  2691.     else                                                    // Abort if no volumes installed for drive        
  2692.     {
  2693.         return(false);
  2694.     }
  2695.  
  2696.     return(true);
  2697. }
  2698.  
  2699.  
  2700. //------------------------------------------------------------------------------
  2701. //    Function:        RemoveDrive
  2702. //    Description:    Removes a physical drive and its volumes from our control
  2703. //                    
  2704. //-------------------------------------------------------------------------------
  2705. void RemoveDrive(DriveRecPtr theDrivePtr)
  2706. {        
  2707.     VolumeRecPtr    vol;
  2708.     UInt16            vrefnum;
  2709.     
  2710.  
  2711.     if (theDrivePtr)                                // If the drive exists…
  2712.     {
  2713.         while (theDrivePtr->nextVol)                // dequeue all volumes on the drive…
  2714.         {
  2715.             vol = theDrivePtr->nextVol;                // get volume
  2716.             vrefnum = vol->vRefNum;                    // get its refnum
  2717.             RemoveVolume(theDrivePtr, vrefnum);        // delete volume from our queue
  2718.         }
  2719.     }
  2720. #if 0
  2721.     RemovePowerManagement();            // Remove power management routines and VBLs
  2722. #endif
  2723. }
  2724.  
  2725.  
  2726. //------------------------------------------------------------------------------
  2727. //    Function:        NextQDrive
  2728. //
  2729. //    Description:    Returns the next unused logical drive number from the system 
  2730. //                    Drive Queue.  The Drive Queue is searched starting with a
  2731. //                    logical drive number 8.
  2732. //                    
  2733. //    Input:            none
  2734. //
  2735. //    Output:            The highest Drive Queue drive number + 1
  2736. //-------------------------------------------------------------------------------
  2737. SInt16 NextQDrive( void )
  2738. {
  2739.     QHdrPtr    qhdr = GetDrvQHdr();                // Pointer to Drive Queue 
  2740.     DrvQEl    *qel = (DrvQEl*) (qhdr->qHead);        // Pointer to first element 
  2741.     SInt16    drv = 8;                            // Start above built in drives 
  2742.  
  2743.     while (qel)                                 // While not end of queue, 
  2744.     {
  2745.         if (qel->dQDrive == drv)                 // if drive number used, 
  2746.         {
  2747.             drv++;                                // bump number, and 
  2748.             qel = (DrvQEl*) (qhdr->qHead);        // search from start 
  2749.         }
  2750.         else                                    // else next queue element 
  2751.             qel = (DrvQEl*) (qel->qLink);
  2752.     }
  2753.  
  2754.     return(drv);                        // Return the next logical drive 
  2755. }
  2756.  
  2757.  
  2758. //------------------------------------------------------------------------------
  2759. //    Function:        FlushDriveWriteCache
  2760. //
  2761. //    Description:    Flushes the drive's write cache
  2762. //                    
  2763. //    Input:            None.
  2764. //
  2765. //    Output:            None.
  2766. //-------------------------------------------------------------------------------
  2767. void FlushDriveWriteCache( void )
  2768. {
  2769.     // Unfortunately, there is no consistent method for flushing the write cache on
  2770.     // all drives.  However, all drives should flush the cache within a couple of
  2771.     // rotations so we can simply hang out for a bit and hope the cache is flushed.
  2772.     // Use a non-interrupt timer since interrupts may be disabled at this time.
  2773.     AbsoluteTime    theWait;
  2774.     
  2775.     theWait = DurationToAbsolute(durationMillisecond*500);
  2776.     DelayForHardware( theWait );            // delay for max seek and a few rotations (500 msec.)
  2777. }
  2778.  
  2779.  
  2780. //------------------------------------------------------------------------------
  2781. //    Function:        FindMountedVol
  2782. //    Description:    Searches the volume queue for a mounted volume specified 
  2783. //                    by vRefNum.  If one is found, its VCB pointer is returned.
  2784. //                    
  2785. //    Input:            vRefNum:    the volume reference to search for
  2786. //
  2787. //    Output:            the VCB pointer (NULL = volume not mounted)
  2788. //-------------------------------------------------------------------------------
  2789. static VCB *FindMountedVol(SInt16 vRefNum)
  2790. {
  2791.     QHdrPtr    volQ = GetVCBQHdr();        // VCB queue head pointer
  2792.     VCB        *theVol = (volQ) ? (VCB*)volQ->qHead : 0;    // first VCB
  2793.  
  2794.     while(theVol)
  2795.     {
  2796.         // The test for whether a volume is mounted or not is done using
  2797.         // the VCB fields vcbDrvNum and vcbDRefNum.  A volume is mounted
  2798.         // only if it is online.
  2799.         //
  2800.         //                    online            offline            ejected
  2801.         //
  2802.         //    vcbDrvNum        >0 (DrvNum)         0                 0
  2803.         //    vcbDRefNum        <0 (DRefNum)    <0 (-DrvNum)    >0 (DrvNum)
  2804.     
  2805.         if (theVol->vcbDrvNum == vRefNum)    // if volume specified is online…
  2806.             break;                            // volume is mounted
  2807.  
  2808.         theVol = (VCB*)theVol->qLink;        // next VCB
  2809.     }
  2810.     
  2811.     return(theVol);
  2812. }
  2813.  
  2814. //------------------------------------------------------------------------------
  2815. //    Function:        MountedVolOfDrive
  2816. //
  2817. //    Description:    Searches the system VCB for a mounted volume on the specified 
  2818. //                    physical drive.
  2819. //                    
  2820. //    Input:            drive:    pointer to the physical drive's record
  2821. //
  2822. //    Output:            the VCB pointer (nil if no volume mounted for drive)
  2823. //-------------------------------------------------------------------------------
  2824. VCB * MountedVolOfDrive(DriveRecPtr drive)
  2825. {
  2826.     VolumeRecPtr    vol = nil;
  2827.     VCB                *vcb = nil;
  2828.     
  2829.     if (drive)
  2830.     {
  2831.         vol = drive->nextVol;                // first volume (logical drive) of drive
  2832.         while (vol)
  2833.         {
  2834.             vcb = FindMountedVol(vol->vRefNum);        // check if a volume is mounted
  2835.             if (vcb)                                // if mounted volume, stop now
  2836.                 break;
  2837.             else                                    // next volume of drive
  2838.                 vol = (VolumeRecPtr)vol->nextVol;
  2839.         }
  2840.     }
  2841.     
  2842.     return(vcb);
  2843. }
  2844.  
  2845.  
  2846. //------------------------------------------------------------------------------
  2847. //    Function:        UpdateQ
  2848. //    Description:    Updates the specified drive in the system drive queue with
  2849. //                    the specified capacity
  2850. //                    
  2851. //    Input:            qDrive:        the drive to update
  2852. //                    newSize:    the new drive capacity
  2853. //-------------------------------------------------------------------------------
  2854. void UpdateQ(SInt16 qDrive, SInt32 newSize)
  2855. {
  2856.     QHdrPtr qhdr = GetDrvQHdr();                // Pointer to Drive Queue 
  2857.     DrvQEl *qel = (DrvQEl*) (qhdr->qHead);        // Pointer to first element 
  2858.  
  2859.     while (qel) 
  2860.     {
  2861.         // Search until drive is found, then update its capacity
  2862.         if (qel->dQDrive == qDrive)                // if drive number found, 
  2863.         {        
  2864.             qel->dQDrvSz2 = *(UInt16*)&newSize;    // new capacity (hi word)
  2865.             qel->dQDrvSz = newSize;                        // low word of capacity 
  2866.             break;
  2867.         }
  2868.         else                            // else next queue element 
  2869.             qel = (DrvQEl*) (qel->qLink);
  2870.     }
  2871. }
  2872.  
  2873. //------------------------------------------------------------------------------
  2874. //    FUNCTION:    NextPartitionID
  2875. //    PURPOSE:    Returns the next unique partition ID for all volumes associated
  2876. //                with the specified drive.  NOTE: This function should be used only
  2877. //                when adding volumes which do not have a partition map on the media.
  2878. //                
  2879. //    INPUT:        drive:    pointer to the drive record to search for next partition ID
  2880. //
  2881. //    OUTPUT:        a unique partition ID related to the specified volume
  2882. //
  2883. //-------------------------------------------------------------------------------
  2884. SInt32 NextPartitionID(DriveRecPtr drive)
  2885. {
  2886.     VolumeRecPtr vol = 0;
  2887.     SInt32 nextPartitionID = 1;                // Start search with partition ID of 1
  2888.  
  2889.     if (drive)                                // if drive is present…
  2890.     {
  2891.         vol = (VolumeRecPtr)drive->nextVol;            // first volume pointer
  2892.  
  2893.         while (vol)                            // while not end of volume queue…
  2894.         {
  2895.             if (vol->partitionNo == nextPartitionID)    // if partition ID used
  2896.             {
  2897.                 nextPartitionID++;                    // bump partition ID
  2898.                 vol = drive->nextVol;                // reset volume pointer
  2899.             }
  2900.             else
  2901.                 vol = (VolumeRecPtr)vol->nextVol;            // otherwise, point to next volume
  2902.         }
  2903.     }
  2904.     
  2905.     return(nextPartitionID);            // nil if volume not found
  2906. }
  2907.  
  2908.  
  2909. //------------------------------------------------------------------------------
  2910. //    FUNCTION:    SetPowerMode
  2911. //
  2912. //    PURPOSE:    Sets the power mode on the drive per the New Driver Rules.  The
  2913. //                mode is translated to the closest ATA power mode.
  2914. //
  2915. //    INPUT:        drive:    The physical drive number
  2916. //                powerMode    0 = kMediaModeOn        (normal operation)
  2917. //                                1 = kMediaModeStandBy        (reduced power, ready in 5 sec.)
  2918. //                                2 = kMediaModeSuspend:            (minimal power, ready in 15 sec.)
  2919. //                                3 = kMediaModeOff:        (no power)
  2920. //
  2921. //    OUTPUT:        Returns true if successful
  2922. //
  2923. //    NOTES:        Assumes drive is valid value (drive is present and online)
  2924. //------------------------------------------------------------------------------
  2925. Boolean SetPowerMode(DriveRecPtr drive, UInt16 powerMode, UInt16 timerSecs)
  2926. {
  2927. #pragma unused ( drive, powerMode, timerSecs)
  2928.     IfDebugging("\pSetPowerMode");
  2929.  
  2930.     return noErr;
  2931.  
  2932. #if 0    
  2933.     // Array to map power modes to ATA power commands.  The mapping is as follows:
  2934.     //
  2935.     //    0 (kMediaModeOn)     = Idle Immediate    (reduced power, ready, spinning)
  2936.     //    1 (kMediaModeStandBy)    = Idle Immediate    (reduced power, ready, spinning)
  2937.     //    2 (kMediaModeSuspend)        = Standby Immediate    (lower power, ready, not spinning)
  2938.     //    3 (kMediaModeOff)        = Sleep                (minimal power, not ready, not spinning)
  2939.     UInt16    err;
  2940.     UInt8    PowerCommand[6] = {kATAcmdIdleImmed, kATAcmdIdleImmed, kATAcmdStandbyImmed, kATAcmdSleep, kATAcmdIdle, kATAcmdStandby};
  2941.  
  2942.     // Make call to device here
  2943.  
  2944.     // Set drive status depending upon the power mode command given
  2945.     if (err == noErr)
  2946.         switch(powerMode)
  2947.         {
  2948.             case kMediaModeOn:
  2949.             case kMediaModeStandBy:
  2950.                 drive->isSpunDown = false;        // drive is spinning
  2951.                 break;
  2952.                 
  2953.             case kMediaModeSuspend:
  2954.                 drive->isSpunDown = true;        // drive is not spinning
  2955.                 break;
  2956.                 
  2957.             case kMediaModeOff:
  2958.                 drive->isSpunDown = true;        // drive is not spinning
  2959.                 drive->inSleepMode = true;        // flag sleep mode since we can't check if drive is so
  2960.                 break;
  2961.         }
  2962.             
  2963.     // Some drives may hang if accessed again immediately following a standby or 
  2964.     // idle command.  We can fix this by delaying a bit before the next command.
  2965.     DelayMsec(1);            // delay 1 msec.
  2966.  
  2967.     return((err == noErr) ? true : false);
  2968. #endif
  2969. }
  2970.  
  2971.  
  2972. //--------------------------------------------------------------------------------
  2973. //    Function:        SpinDownDrive
  2974. //
  2975. //    Description:    Spins down the drive for the purpose of conserving power.  No
  2976. //                    action occurs if the drive is already spun down.  This function
  2977. //                    can also be used to flush the drive's write cache.
  2978. //                    
  2979. //    Input:            drive:    Pointer to the drive record
  2980. //
  2981. //    Output:            Updates the curPowerMode variable of the drive record
  2982. //--------------------------------------------------------------------------------
  2983. void SpinDownDrive(DriveRecPtr drive)
  2984. {
  2985.     // If the drive is not spun down then issue an ATA Standby command (kMediaModeSuspend) to 
  2986.     // spin it down.  We use the Standby command instead of the Sleep command since 
  2987.     // some drives (Quantum Fireball) take too long to spindown.
  2988.  
  2989.     if (!drive->isSpunDown)
  2990.     {
  2991.         SetPowerMode(drive, kMediaModeSuspend, 0);         // put drive in standby mode
  2992.     }
  2993. }
  2994.  
  2995.  
  2996. //------------------------------------------------------------------------------
  2997. //    FUNCTION:    CheckUnexpectedRemoval
  2998. //    PURPOSE:    Checks for offlined drives and offlines its volumes.
  2999. //                NOTE: Assumes it is called during non-interrupt time.
  3000. //
  3001. //------------------------------------------------------------------------------
  3002. void CheckUnexpectedRemoval(DriveRecPtr drive)
  3003. {
  3004.     VolumeRecPtr vol;
  3005.     
  3006.     if (drive)                        // if drive is present…
  3007.     {
  3008.         if (drive->busState == kOFFLINE)        // drive offline?
  3009.         {
  3010.             // Put all of the drive's volumes offline…
  3011.             
  3012.             vol = drive->nextVol;                // first volume of drive
  3013.             while (vol)                            // while more volumes…
  3014.             {
  3015.                 if (vol->driveStatus.diskInPlace > 0)    // if volume online…
  3016.                 {
  3017.                     //vol->driveStatus.diskInPlace = 0;    // offline volume;
  3018.                     vol->driveStatus.diskInPlace = -1;    // offline volume;
  3019.                     //vol->driveStatus.installed = 0;        // no drive installed
  3020.                     vol->driveStatus.installed = -1;        // no drive installed
  3021.                     // If the driver was reentrant, we could do a PBOffline to offline
  3022.                     // the volume and leave the ghost image, but since we are not
  3023.                     // reentrant, we use the little known -2 disk insert
  3024.                     // event for manual ejection which will either put up a "Please
  3025.                     // insert the disk…" message or beep if not supported.
  3026.                     //PostEvent(diskEvt, ((-2 << 16) | vol->vRefNum));
  3027.                     PostEvent(diskEvt, (vol->vRefNum));
  3028.                 }
  3029.                 
  3030.                 vol = (VolumeRecPtr)vol->nextVol;            // next volume pointer
  3031.             }
  3032.         }
  3033.     }
  3034. }
  3035.  
  3036.  
  3037. //------------------------------------------------------------------------------
  3038. //    Function:        MountUnmountVolumes
  3039. //    Description:    Mounts and unmounts all volume for the specified drive.
  3040. //
  3041. //    Input:            DriveRecPtr drive:    drive with volumes to mount/unmount
  3042. //
  3043. //    Output:            Returns any errors that occur from PostEvent
  3044. //-------------------------------------------------------------------------------
  3045. OSErr MountUnmountVolumes( DriveRecPtr drive )
  3046. {
  3047.     VolumeRecPtr        vol;
  3048.     OSErr                mountErr = noErr;
  3049.     
  3050.     // First check for unexpected drive removal.  Volumes will be deleted
  3051.     // if the drive is not present so we don't try to mount them below.
  3052.     CheckUnexpectedRemoval(drive);
  3053.                 
  3054.     // Post a Disk Inserted event for all HFS volumes not yet mounted
  3055.     if (drive)                                                        // for each drive…
  3056.     {
  3057.         vol = drive->nextVol;                                        // first volume structure
  3058.         while (vol)                                                 // for all volumes on drive…
  3059.         {                            
  3060.             if ((vol->driveStatus.diskInPlace != 0) &&                // if media in place,
  3061.                (vol->mountthispart) &&                                 // and volume to be mounted,
  3062.                (!vol->partmounted))                                    // and hasn't been done yet
  3063.             {
  3064.                 mountErr = PostEvent(diskEvt, vol->vRefNum);
  3065.  
  3066.                 if ( mountErr == noErr )
  3067.                 {
  3068.                     vol->partmounted = true;
  3069.                 }
  3070.             }
  3071.  
  3072.             vol = (VolumeRecPtr)vol->nextVol;                        // next per volume pointer
  3073.         }
  3074.     }
  3075.  
  3076.     return mountErr;                                                // return error if one occurred
  3077. }
  3078.